/* =========================================================================
   SLEEP MAGAZINE — Style C · "Stark" (bold editorial magazine)
   Shared design system. Theme-able via assets/style-c.js (accent/bg/font/density).
   ========================================================================= */
/* Fonts are self-hosted — see assets/css/fonts.css (GDPR: no Google Fonts CDN). */
/* -------------------------------------------------------------------------
   TOKENS — the whole look lives in this block.
   Anything that is not a var() here cannot be restyled or themed, so do not
   reintroduce raw px/hex below. The five groups marked [JS] are swapped at
   runtime by assets/js/style-c.js (accent / bg / font / ink / density).
   ------------------------------------------------------------------------- */
:root{
  /* ── colour: base ──────────────────────────────────────────── [JS: ink] */
  --ink:#121212; --soft:#454440; --mute:#6c6b64;
  --paper:#f4f1e8; --card:#ffffff; --dark:#161512; --dark-alt:#241d2e;
  --line:#1c1c1c; --hair:#ddd8c9;
  --hi:#f4d23c; --hi-2:#ec6240; --hi-3:#2bb3a3; --blue:#00518d;
  --pos:#1f7a44; --neg:#c63a26;

  /* ── colour: on dark surfaces ─────────────────────────────── [derived]
     Text/borders that sit ON --dark (footer, dark heroes, verdict box,
     rabatte hub, mega feature). These were 35 hardcoded warm greys tuned
     for creme+schwarz, so every other ink/bg preset put beige text on a
     cool ground. They turn out to be a plain --paper -> --dark ramp at
     100/80/50/16/10 %, so the @supports block below reproduces them to
     within ~2/255 AND makes them track both switchers for free.
     These static values are the no-color-mix fallback. */
  --on-dark:#f4f1e8; --on-dark-soft:#c9c6ba; --on-dark-mute:#86847a;
  --on-dark-line:#3a352c; --on-dark-hair:#2c2a24;

  /* ── colour: derived surfaces ──────────────────────────────────────────*/
  --tint-hi:oklch(0.96 0.05 95);        /* winner row, subtle --hi wash    */
  --tint-cool:oklch(0.92 0.02 95);      /* firmness scale fill             */
  --scrim:oklch(0.18 0.02 256 / .82);   /* caption over image              */

  /* ── type ─────────────────────────────────────────────────── [JS: font] */
  --serif:"Playfair Display", Georgia, serif;
  --sans:"IBM Plex Sans", system-ui, sans-serif;
  /* A snapshot of the GLOBAL serif, taken at :root before any component scope
     shadows --serif. A custom property cannot be read once a descendant
     redefines it, so this is the only way for a component inside a scope that
     pins --serif (.sm-arch) to opt back out — which is what the Classic card
     option needs, so an old-style card looks the same in the archive as it does
     on the homepage. Resolves against whatever :root ends up with, including
     the Customizer's Design-tokens override. */
  --serif-root:var(--serif);
  --prose-ink:#33302b;
  /* The ramp mirrors the sizes the design already used, value for value —
     it is NOT an idealised scale. Re-proportioning type is a design call
     for the revamp, not something a refactor should do silently, so the
     only values that moved here were the 15 half-pixel sizes (9.5-18.5px),
     snapped to their integer neighbour. Change a step below and every one
     of the 280 declarations that reference it follows. */
  --fs-3xs:10px;  --fs-2xs:11px; --fs-xs:12px;  --fs-sm:13px;
  --fs-md:14px;   --fs-base:15px; --fs-lg:16px; --fs-xl:17px;
  --fs-2xl:18px;  --fs-3xl:20px; --fs-4xl:24px; --fs-5xl:30px;
  --fs-h1:clamp(44px,6.4vw,86px);
  --fs-h2:clamp(32px,4.2vw,50px);
  --fs-h3:clamp(22px,2.6vw,30px);
  /* 70 one-off display sizes (19-96px) stay literal on purpose: each belongs
     to a single component (hero, bigscore, stat) and folding them into steps
     would change the page's proportions. They are the revamp's decision. */

  /* ── space ─────────────────────────────────────────────── [JS: density] */
  --sp-1:4px;  --sp-2:8px;  --sp-3:12px; --sp-4:16px; --sp-5:20px;
  --sp-6:26px; --sp-7:34px; --sp-8:48px; --sp-9:64px;
  --space-sec:78px;

  /* ── radius ────────────────────────────────────────────────────────────
     The Stark design is square, so this is 0 and nothing rounds. It exists
     so the Customizer token page can preview rounded corners site-wide
     without editing 60 rules. --radius-btn lets CTAs round on their own. */
  --radius:0; --radius-btn:var(--radius);

  /* ── buttons ───────────────────────────────────────────────────────────
     Base .btn only. The --o/--hi/--light variants deliberately override
     these with their own colourways. */
  --btn-bg:var(--ink); --btn-fg:#fff; --btn-border:var(--ink);
  --btn-hover-bg:var(--hi); --btn-hover-fg:var(--ink);

  /* ── frame & shadow ────────────────────────────────────────────────────
     2px is the Stark signature frame; 3px is the bold divider/emphasis. */
  --frame:2px; --frame-bold:3px; --frame-hair:1px;
  /* --frame-light is the OFF SWITCH for the frames we decided the page has
     too many of (2026-08): the live-deal banners, the mega-menu star card and
     its above/below-list rules, the product card's inner separators, the
     review sidebar rail, gallery tiles, the score panel and the spec table.
     Those rules were not deleted — they were moved onto this token, so
     raising it to 1px (or 2px) brings every one of them back at once, here or
     from Design > Design tokens. Everything still on --frame is a frame we
     mean to keep. */
  --frame-light:0;
  /* A divider colour that SURVIVES. The site currently runs --hair:#ffffff and
     --frame-hair:0px as token overrides (Design > Design tokens), so every
     hairline in this file is invisible in production — which is why only the
     2px/3px frames were left doing the lining. The few dividers we keep on
     purpose (the score panel's split) are drawn with this instead, at a literal
     1px, so neither override can erase them. Derived from --line below, so it
     follows the ink preset; the literal is the fallback. */
  --hair-soft:#dfe1e6;
  /* The nav's light hairline, promoted out of .nav--light (2026-08-09) so it is
     a real global token: tables draw their row dividers with it. Same reason as
     --hair-soft — it is a literal rgba, so neither the --hair:#fff nor the
     --frame-hair:0 token override can erase it. */
  --nav-line:rgba(0,0,0,.14);
  /* Hard offset shadows, numbered by weight. Same fidelity rule as the type
     ramp: these are the six offsets the design actually used, not a snapped
     scale, so nothing moved. Restyling the shadow = editing these 8 lines. */
  --shadow-1:2px 2px 0 var(--ink);
  --shadow-2:3px 3px 0 var(--ink);
  --shadow-3:4px 4px 0 var(--ink);
  --shadow-4:5px 5px 0 var(--ink);
  --shadow-5:6px 6px 0 var(--ink);
  --shadow-6:8px 8px 0 var(--ink);
  --shadow-hi:6px 6px 0 var(--hi);
  --shadow-hi-sm:3px 3px 0 var(--hi);
  --shadow-line:3px 3px 0 var(--line);

  /* ── layout & stacking ─────────────────────────────────────────────────
     --nav-h / --subnav-h are overwritten at runtime with the real measured
     heights by style-c.js; these are the pre-JS defaults. They matter: a URL
     that arrives with a #hash is scrolled by the browser at parse time, long
     before the footer script runs, so a default that is 64px short parks the
     target under the sticky bar. See the .nav--st override below. */
  --nav-h:70px; --subnav-h:0px;
  --z-skip:20;   --z-subnav:25; --z-fbar:26; --z-nav:30;  --z-topbar:31;
  --z-megabg:39; --z-panel:40;   --z-navrow:41;
  /* --z-fbar sits BELOW --z-nav on purpose: the archive filter bar is sticky
     chrome that pins UNDER the navigation, exactly like --z-subnav. It briefly
     used --z-panel (40) and painted straight over the header. */
  --z-suggest:70; --z-overlay:90; --z-toast:1000;
  /* ABOVE --z-toast on purpose. The mobile search modal is a sibling of
     <header class="nav">, and while the burger menu is open the header is
     lifted to --z-toast (see `.nav:has(.mobile-nav.is-open)` in the ≤980px
     block) so the promo topbar cannot paint over the menu. A search modal at
     --z-overlay therefore opened UNDERNEATH the menu it was launched from:
     .mobile-nav's 90 ranks inside the header, the header's 1000 is what the
     modal actually competes with. Comparing the two z-index NUMBERS is the
     trap — they are not in the same stacking context. */
  --z-searchmodal:1010;
}
/* Pre-JS --nav-h for the stacked nav: two rows above 980px (the search row plus
   the menu row) measure 134px, and below it row2 is hidden so the 70px default
   is already right. Without this an anchor link opened cold lands 64px high. */
@media (min-width: 981px){ :root:has(.nav--st){--nav-h:134px} }
/* Density scales the whole spacing ramp, not just the section gap — that is
   what makes "Kompakt"/"Eng" visibly change the page. */
html[data-density="kompakt"]{
  --space-sec:54px;
  --sp-5:16px; --sp-6:20px; --sp-7:26px; --sp-8:36px; --sp-9:48px;
}
html[data-density="eng"]{
  --space-sec:40px;
  --sp-4:12px; --sp-5:14px; --sp-6:16px; --sp-7:20px; --sp-8:28px; --sp-9:36px;
}
/* The real on-dark definition: derive from the two tokens the theme panel
   actually swaps, so dark surfaces follow every ink AND bg preset. */
@supports (color: color-mix(in srgb, red 50%, blue)) {
  :root{
    --on-dark:      var(--paper);
    --on-dark-soft: color-mix(in srgb, var(--paper) 80%, var(--dark));
    --on-dark-mute: color-mix(in srgb, var(--paper) 50%, var(--dark));
    --on-dark-line: color-mix(in srgb, var(--paper) 16%, var(--dark));
    --on-dark-hair: color-mix(in srgb, var(--paper) 10%, var(--dark));
    --hair-soft:    color-mix(in srgb, var(--line) 16%, var(--card));
  }
}

/* ── thin-border: the frame weight as an opt-in scope ───────────────────────
   Only the WEIGHT changes. Colours, radii (still square), shadows and spacing
   are untouched, so this is a scope class and not a skin. It works because the
   frames are already fully tokenised — there is no hardcoded `Npx solid` left
   anywhere in the nav — so redefining the two tokens on an ancestor rethreads
   every frame inside it and nothing outside it.

   Put it on <header class="nav"> for the navigation alone, or on <body> for the
   whole page; Customizer → Sleep Magazine Design → Border weight does both.
   --frame-hair and --frame-light are deliberately NOT touched: the hairline is
   already 1px (and 0 in production via a token override), and --frame-light is
   the frames-off switch, not a weight. */
.thin-border{--frame:1px;--frame-bold:1px}

*{box-sizing:border-box}
/* a11y utilities (moved from the retired main.css) */
.screen-reader-text{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.skip-link:focus{position:fixed;z-index:var(--z-skip);top:12px;left:12px;width:auto;height:auto;padding:10px 14px;clip:auto;color:#fff;background:var(--ink)}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);font-size:var(--fs-xl);line-height:1.6;-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{font-family:var(--serif);margin:0;font-weight:800;line-height:1.0;letter-spacing:-0.01em}
p{margin:0}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
.wrap{max-width:1160px;margin:0 auto;padding:0 32px}
.wrap--narrow{max-width:880px}
.kicker{font-family:var(--sans);font-size:var(--fs-xs);font-weight:700;letter-spacing:.18em;text-transform:uppercase}
.kicker + h2{margin-top:10px}
.hl{background:var(--hi);padding:0 .12em;box-decoration-break:clone;-webkit-box-decoration-break:clone}
.sec{padding:var(--space-sec) 0}
.sec--tight{padding-top:24px} /* when a hero directly above already provides the air */
.sec--card{background:var(--card)}
.sec--dark{background:var(--dark);color:var(--paper)}
.sec__head{display:flex;align-items:flex-end;justify-content:space-between;border-bottom:var(--frame-bold) solid var(--line);padding-bottom:18px;margin-bottom:36px;gap:20px;flex-wrap:wrap}
.sec--dark .sec__head{border-color:var(--paper)}
.sec__head h2{font-size:var(--fs-h2)}
.sec__lead{color:var(--soft);font-size:var(--fs-2xl);max-width:56ch;margin-top:14px}

/* ---- top bar ---- */
/* z-index 31 > .nav's 30: keeps the promo line above the stacked layout's
   fixed scrim, which otherwise dims its accent color while a panel is open */
.topbar{background:var(--hi);color:var(--ink);font-weight:700;font-size:var(--fs-sm);letter-spacing:.04em;text-align:center;padding:9px;text-transform:uppercase;position:relative;z-index:var(--z-topbar)}

/* ---- nav ---- */
.nav{position:sticky;top:0;z-index:var(--z-nav);background:var(--paper);border-bottom:var(--frame) solid var(--line)}
.nav__row{display:flex;align-items:center;justify-content:space-between;height:70px}
.logo{font-family:var(--serif);font-weight:900;font-size:var(--fs-4xl);letter-spacing:-0.02em;white-space:nowrap}
.logo em{font-style:italic;font-weight:600}
.logo img{display:block;height:38px;width:auto}
.foot .logo img{height:40px}
.nav__links{display:flex;gap:24px;font-weight:600;font-size:var(--fs-md)}
.nav__links a{color:var(--soft)} .nav__links a:hover,.nav__links a.is-active{color:var(--ink)}
.nav__links a.is-active{box-shadow:inset 0 -3px var(--hi)}

/* ---- buttons ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--sans);font-weight:700;font-size:var(--fs-base);background:var(--btn-bg);color:var(--btn-fg);padding:15px 24px;border:var(--frame) solid var(--btn-border);cursor:pointer;transition:.15s;white-space:nowrap}
.btn:hover{background:var(--btn-hover-bg);color:var(--btn-hover-fg)}
.btn--o{background:transparent;color:var(--ink)} .btn--o:hover{background:var(--ink);color:#fff}
.btn--hi{background:var(--hi);color:var(--ink);border-color:var(--ink)} .btn--hi:hover{background:var(--ink);color:#fff}
.btn--light{background:#fff;color:var(--ink);border-color:#fff} .btn--light:hover{background:var(--hi);border-color:var(--hi)}
.btn--block{width:100%}
.btn--lg{padding:18px 30px;font-size:var(--fs-lg)}
.btn--sm{padding:10px 15px;font-size:var(--fs-sm)}
.tlink{font-weight:700;border-bottom:var(--frame-bold) solid var(--hi);padding-bottom:2px}
.tlink:hover{background:var(--hi)}
.anz{font-size:var(--fs-xs);letter-spacing:.01em;color:var(--mute);margin-top:8px;text-align:center;font-weight:600}

/* ---- hero ---- */
.hero{padding:84px 0 76px;position:relative}
.hero--dark{background:var(--dark);color:var(--paper)}
.hero--dark .kicker{color:var(--hi)}
.hero--dark h1{color:#fff} .hero--dark .hero__lead{color:var(--on-dark-soft)}
.hero--light{background:var(--paper);border-bottom:var(--frame-bold) solid var(--line)}
.hero--light .kicker{color:var(--hi-2)}
.hero h1{font-size:var(--fs-h1);max-width:16ch;margin:20px 0 0}
.hero--product h1{font-size:clamp(26px,3.2vw,44px);max-width:none;word-break:normal}
/* Brand hero: the sitewide 16ch cap is narrower than "{Brand} Gutscheincode"
   on its own, so the phrase always broke mid-title. Full width plus a calmer
   size keeps it on line 1; the italic subtitle takes line 2, and no more. */
.hero--compact{padding:46px 0 40px}
/* The crumbs bar already separates the hero from the nav, so the full 84px
   top padding read as dead space under it. Bottom padding stays — the lead
   and tag rows need it. Two selectors deep, so the responsive .hero padding
   shorthands further down can't undo it. */
.crumbs + .hero{padding-top:38px}
/* Comparison hero: same 16ch problem .hero--brand and .hero--product already
   solved, and for the same reason — one German compound is wider than the cap.
   "Taschenfederkernmatratze" is 673px at 54px against a 518px box (16ch) and a
   598px text track, so its last letter painted 19px onto the hero image above
   ~1200px, where .wrap stops growing but 4.4vw does not. It is the longest
   token in the published set (24 chars), so sizing for it covers all of them.
   Breaking the word is not the fix: hyphens:auto produces NO hyphenation here
   (Chrome's dictionary is a downloadable component, absent — same finding as
   the .tbl--compact note further down) and overflow-wrap:break-word does not
   lower min-content width at all. So: drop the cap, and cap the size instead.
   3.9vw keeps the word inside its track at 900-1100px too, which stops the
   grid from stealing width from the image to fit it. */
.hero--compact h1{font-size:clamp(34px,3.9vw,46px);max-width:none;margin-top:14px}
.hero--compact .hero__lead{font-size:var(--fs-2xl);margin-top:16px}
.hero--compact .hero__meta{margin-top:24px;gap:28px}
.hero--compact .hero__meta div b{font-size:27px}
.hero__grid{display:grid;grid-template-columns:1.15fr .85fr;gap:56px;align-items:center}
.hero__grid h1{margin-top:18px}
.hero h1 .it{font-style:italic;font-weight:600}
.hero__lead{color:var(--soft);font-size:var(--fs-3xl);max-width:54ch;margin-top:24px}
.hero__cta{display:flex;gap:14px;margin-top:34px;flex-wrap:wrap}
.hero__meta{display:flex;gap:34px;margin-top:38px;flex-wrap:wrap}
.hero__meta div b{font-family:var(--serif);font-size:34px;font-weight:800;display:block;line-height:1}
.hero--dark .hero__meta div b{color:#fff}
.hero__meta div span{font-size:var(--fs-xs);letter-spacing:.1em;text-transform:uppercase;color:var(--mute)}
/* --mute is unreadable on the dark hero ("Produkte getestet" etc.) */
.hero--dark .hero__meta div span{color:var(--on-dark-soft)}
.hero--dark .byline small{color:var(--on-dark-soft)}

/* ---- breadcrumbs (Customizer > Breadcrumbs) --------------------------------
   ONE global trail. Its colour is NOT fixed: it reads two variables the
   surrounding band sets, so the same markup works on a light band, a dark band
   or inside another component's header without a second skin and without the
   JS the prototype used to guess its background.

     --sm-crumb         normal links + separators
     --sm-crumb-strong  the current page (and link hover)

   Defaults below are the light-band values, so any band that sets nothing keeps
   the appearance it had. A dark band sets them to rgba(255,255,255,.72)/#fff —
   see .crumbs--on-dark and the archive head. */
.crumbs{border-bottom:var(--frame-hair) solid var(--hair);background:var(--paper)}
/* padding-top/bottom ONLY — a `padding:11px 0` shorthand would wipe .wrap's
   horizontal gutter and pin the trail to the left screen edge on mobile */
.crumbs .wrap{display:flex;align-items:center;gap:8px;padding-top:11px;padding-bottom:11px;font-size:var(--fs-sm);color:var(--sm-crumb,var(--mute));flex-wrap:wrap}
.crumbs a{color:var(--sm-crumb,var(--soft));text-decoration:none;font-weight:600}
.crumbs a:hover{color:var(--sm-crumb-strong,var(--ink));text-decoration:underline}
.crumbs i{font-style:normal;color:var(--sm-crumb,var(--mute));opacity:.5}
.crumbs span[aria-current]{color:var(--sm-crumb-strong,var(--ink));font-weight:600}
/* Bare variant: no band of its own — inherits the surrounding section's
   background and sits flush in its padding. Used by the archive page head.
   The inner .wrap has to give up BOTH its gutter and its max-width: this
   variant is nested inside another .wrap, so keeping them indented the trail
   32px further than the h1 it sits above. */
.crumbs--bare{background:none;border-bottom:0}
.crumbs--bare .wrap{padding:0;max-width:none}
/* Any band that paints itself dark opts in with this class. */
.crumbs--on-dark{--sm-crumb:rgba(255,255,255,.72);--sm-crumb-strong:#fff}
.hero__tags{display:flex;gap:10px;margin-top:28px;flex-wrap:wrap}
.hero__tags .stamp{margin-bottom:0}
.hero__logo-row{display:flex;align-items:center;gap:22px;flex-wrap:wrap;margin-top:18px}
.hero__brandlogo{display:block;width:130px;height:74px;border:var(--frame) solid var(--line);object-fit:contain;background:var(--card);padding:8px}

/* ---- stats (homepage hero) ---- */
.stats{display:grid;grid-template-columns:repeat(2,1fr);border:var(--frame) solid var(--paper)}
.hero--dark .stats{border-color:var(--on-dark-line)}
.stat{padding:26px;border:var(--frame-hair) solid var(--on-dark-line)}
.stat b{font-family:var(--serif);font-weight:900;font-size:40px;color:var(--hi);display:block;line-height:1}
.stat span{font-size:var(--fs-sm);color:var(--on-dark-soft);margin-top:8px;display:block}

/* ---- brand strip ---- */
.brands{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));border-top:var(--frame) solid var(--line);border-left:var(--frame) solid var(--line)}
.brandchip{display:flex;align-items:center;justify-content:center;min-height:96px;padding:18px 20px;background:var(--card);border-right:var(--frame) solid var(--line);border-bottom:var(--frame) solid var(--line);font-family:var(--serif);font-weight:700;font-size:var(--fs-base);text-align:center;color:var(--ink);transition:background .12s}
.brandchip:hover{background:var(--paper)}
.brandchip img{max-width:100%;height:auto}

/* ---- product card grid ----
   The container frames the grid while every cell also draws its own right +
   bottom line, so the container's bottom edge doubled to 4px against the last
   row's cells. The container now skips its bottom edge and lets the cells own
   it (they always did). Directly under a .sec__head the top edge is dropped
   too — same double-rule problem `.sec__head + .item` already solves: the
   section's own 3px rule was landing 36px above a second full-width line
   across the top of the cards ("Weitere Produkte" on a review). */
.grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border:var(--frame) solid var(--line);border-bottom:0}
.grid-3 > *{background:var(--card);border-right:var(--frame) solid var(--line);border-bottom:var(--frame) solid var(--line)}
.grid-3 > *:nth-child(3n){border-right:0}
.sec__head + .grid-3{border-top:0}
/* Article lattice on the hairline, like the FAQ rows and the tables.
   ---------------------------------------------------------------------------
   The article archives (/blog/, post/category/author archives, the front page
   row, related articles on a single) drew this shared lattice in the 2px ink
   frame, so a page of cards read as a heavy grid of boxes. Same call as the FAQ
   separators: a line that DIVIDES goes on the 1px --nav-line the tables and the
   light nav use; a line that FRAMES stays as it is.

   Done by re-declaring the two tokens on the grid's own scope rather than by
   restating the borders, which is the .thin-border trick (see :root) and the
   only version that survives: the responsive rules further down re-assert
   `border-right: var(--frame) solid var(--line)` at 2 columns and
   `border-right: 0` at 1, so any override that hard-codes a width either loses
   to them or wins where it must not (a 1px right edge on single-column cards).
   Re-pointing the tokens leaves every one of those rules saying exactly what it
   already says, one weight and one colour lighter.

   :has(> .bcard) is what keeps this to article cards: the same bare .grid-3 is
   the brand-card lattice in archive-sm_brand.php, which nobody asked to change.
   The chip and the "Artikel lesen" underline pin --frame back to 2px — neither
   is a separator, so both keep the weight they were designed with. */
.grid-3:has(> .bcard){--frame:1px;--line:var(--nav-line)}
.grid-3:has(> .bcard) .bcard__cat,
.grid-3:has(> .bcard) .bcard__more{--frame:2px}
.grid-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px}

/* ---- .grid-3--cards: gapped grid for self-bordered cards (2026-08 refonte) ----
   Plain .grid-3 draws ONE shared frame and lets its children share edges. The
   redesigned .pcard carries its own hairline border, so inside .grid-3 it would
   double every rule. This modifier drops the container frame and the shared
   child edges and replaces them with a real gap.

   It is opt-in per call site rather than a change to .grid-3 itself, because
   .grid-3 is also the blog (.bcard) and brand-card grid and those still use the
   shared-frame look. Every product-card grid carries it; nothing else does. */
.grid-3--cards{border:0;gap:20px}
/* The :nth-child(3n) selector below is not decoration — .grid-3 drops the right
   edge of every third cell at (0,2,0), which OUT-SPECIFIES .pcard's own
   `border:1px` at (0,1,0). Without it the last card in each row silently loses
   its right border. Reset the shared edges, then restore the card's own frame
   at a specificity that survives. */
.grid-3--cards > *,
.grid-3--cards > *:nth-child(3n){border:0;background:none}
/* background restored WITH the border: the generic 3n reset above outranks
   .pcard's own background, so every third card went transparent — invisible
   on white sections, a see-through card on the blue .dealsplit band. */
.grid-3--cards > .pcard,
.grid-3--cards > .pcard:nth-child(3n){border:1px solid var(--hair);background:var(--card)}
.sec__head + .grid-3--cards{border-top:0}

/* ---- pcard: the ONE product card, site-wide (2026-08 refonte) ---------------
   Square, hairline-bordered, no shadow. The three tokens below are re-declared
   on the card's own scope ON PURPOSE: production overrides --hair to #ffffff,
   --radius to 20px and --serif to IBM Plex Sans from Design > Design tokens,
   which would erase this card's border, its square corners and its display
   type. Everything else (ink, --soft, --mute, --hi-2, --dark, --paper) still
   resolves to the live global palette, so the card follows the Customizer
   everywhere it matters and only pins what the design depends on.
   Drop these three lines if the global tokens are ever brought back in line.

   They hang off `body:not(.sm-cards-classic)` rather than `.pcard` itself so
   the Classic card option can fall back to the GLOBAL tokens: a custom property
   cannot be un-set once a selector shadows it, so the only way to make the
   override optional is to not apply it in the first place. */
body:not(.sm-cards-classic) .pcard{
  --hair:#d5dde1;
  --radius:0;
  --serif:"Playfair Display", Georgia, serif;
}
.pcard{background:var(--card);border:1px solid var(--hair);display:flex;flex-direction:column;min-width:0}
.pcard[hidden]{display:none}
.pcard__media{position:relative;height:178px;background:var(--paper);flex:none;overflow:hidden}
.pcard__media img,.pcard__media image-slot,.pcard__media-ph{display:block;width:100%;height:100%;object-fit:cover}
.pcard__body{padding:16px 18px 18px;display:flex;flex-direction:column;gap:9px;flex:1;min-width:0}
/* Editorial best-for tag — outlined, square, never overlaid on the image. */
.pcard__tag{align-self:flex-start;border:1px solid var(--hi-2);color:var(--hi-2);font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:4px 8px;line-height:1.4}
.pcard__head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px}
.pcard__id{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.pcard__brand{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--mute)}
.pcard__title{font-family:var(--serif);font-weight:800;font-size:19px;line-height:1.16;letter-spacing:0;margin:0}
.pcard__score{flex:none;text-align:right;white-space:nowrap}
.pcard__score b{display:block;font-family:var(--serif);font-weight:800;font-size:25px;line-height:1}
.pcard__score small{font-family:var(--sans);font-size:11px;font-weight:500;color:var(--mute);letter-spacing:0}
.pcard__for{color:var(--soft);font-size:13.5px;line-height:1.5}
/* Meta line — accent-coloured, the card's one piece of "why this one" signal. */
.pcard__spec{font-size:12.5px;font-weight:600;color:var(--hi-2);letter-spacing:0}
.pcard__foot{margin-top:auto;border-top:0;padding-top:10px;display:flex;flex-direction:column;gap:9px}
/* --fs-3xl (20px), not the old raw 22px (2026-08-21): the price is the only
   thing on this card that sat off the type scale. --fs-3xl and not --fs-4xl
   (24px) because `body.sm-cards-classic` already bumps the price TO --fs-4xl —
   taking 24px as the default would make that override a no-op while its own
   breakpoint override still dropped classic to 20px. Costs 2px of price. */
.pcard__price{font-family:var(--serif);font-weight:800;font-size:var(--fs-3xl);line-height:1.2}
.pcard__price small{font-family:var(--sans);font-size:var(--fs-xs);font-weight:600;color:var(--mute);letter-spacing:.04em}
/* The card CTA takes the SAME colourway as every other button on the site —
   the --btn-* tokens .btn is built from, so it follows the Customizer's button
   colours instead of inventing its own. It briefly used --dark/--hi-2, which is
   not a pairing the palette actually contains. Only the geometry (full width,
   square, 13px) is the card's own. */
.pcard__cta{display:flex;align-items:center;justify-content:center;gap:8px;background:var(--btn-bg);color:var(--btn-fg);font-family:var(--sans);font-size:14.5px;font-weight:700;padding:13px;text-align:center;transition:background .15s,color .15s}
.pcard__cta:hover{background:var(--btn-hover-bg);color:var(--btn-hover-fg)}
a.pcard:hover .pcard__cta{background:var(--btn-hover-bg);color:var(--btn-hover-fg)}
.pcard__cta:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}

/* ---- Classic product card (Design → Archive → Product card design) ----------
   The pre-refonte card, restored as an opt-in. Markup is IDENTICAL in both
   modes — this is purely a re-skin driven by the `sm-cards-classic` body class,
   so nothing in PHP or JS branches on it.

   Restores: the 2px frame, the 210px image, 24px body padding, the filled tag
   chip, the larger serif title and score, and a rule above the price with the
   standard button as the CTA. In the shared-border grids (.grid-3--cards) it
   also gives the joined frame back, since that is half of what the old card
   looked like. Selectors are body-scoped, so they outrank the refonte rules
   above without a single !important. */
/* Opt back out of .sm-arch's pinned --serif so a classic card in the archive
   matches a classic card anywhere else (see --serif-root at :root). */
body.sm-cards-classic .pcard{--serif:var(--serif-root);border:var(--frame) solid var(--line)}
body.sm-cards-classic .pcard__media{height:auto;background:none;border-bottom:var(--frame-light) solid var(--line)}
body.sm-cards-classic .pcard__media img,
body.sm-cards-classic .pcard__media image-slot,
body.sm-cards-classic .pcard__media-ph{height:210px}
body.sm-cards-classic .pcard__body{padding:24px;gap:12px}
/* the old chip: filled --hi with its own ink frame, not the outlined accent tag */
body.sm-cards-classic .pcard__tag{background:var(--hi);color:var(--ink);border:var(--frame) solid var(--ink);font-size:var(--fs-2xs);letter-spacing:.08em;padding:1px 8px;line-height:1.5;margin-bottom:-2px}
body.sm-cards-classic .pcard__head{gap:18px}
body.sm-cards-classic .pcard__id{gap:5px}
body.sm-cards-classic .pcard__brand{font-size:var(--fs-xs)}
body.sm-cards-classic .pcard__title{font-size:23px;line-height:1.12}
body.sm-cards-classic .pcard__score b{font-weight:900;font-size:var(--fs-5xl)}
body.sm-cards-classic .pcard__score small{display:block;font-size:var(--fs-xs);font-weight:600;letter-spacing:.06em;margin-top:8px;line-height:1.2}
body.sm-cards-classic .pcard__for{font-size:var(--fs-base);line-height:1.6}
/* the old spec line was muted, not the accent meta line */
body.sm-cards-classic .pcard__spec{font-size:var(--fs-sm);color:var(--mute);letter-spacing:.02em}
body.sm-cards-classic .pcard__foot{border-top:var(--frame-light) solid var(--line);padding-top:16px;gap:12px}
body.sm-cards-classic .pcard__price{font-size:var(--fs-4xl)}
/* CTA back to the standard button geometry (.btn --block) */
body.sm-cards-classic .pcard__cta{font-size:var(--fs-base);padding:15px 24px;border:var(--frame) solid var(--btn-border)}
/* …and the joined grid frame the old card was drawn inside */
body.sm-cards-classic .grid-3--cards{gap:0;border:var(--frame) solid var(--line);border-bottom:0}
body.sm-cards-classic .grid-3--cards > .pcard,
body.sm-cards-classic .grid-3--cards > .pcard:nth-child(3n){border:0;border-right:var(--frame) solid var(--line);border-bottom:var(--frame) solid var(--line)}
body.sm-cards-classic .grid-3--cards > .pcard:nth-child(3n){border-right:0}

/* ---- bcard: the ONE blog/Ratgeber card (front page, /blog, post archives) ----
   Accent stripe + category chip pick up --hi/--hi-2 from the Customizer. */
.bcard{background:var(--card);display:flex;flex-direction:column;min-width:0}
/* Accent bar removed: the light-blue (--hi) stripe read as an out-of-place
   "ombre portee" line on the homepage article cards. */
.bcard::before{content:none}
.bcard__media{display:block;position:relative;border-bottom:var(--frame) solid var(--line);overflow:hidden}
.bcard__media img{display:block;width:100%;height:200px;object-fit:cover;transition:transform .25s}
.bcard:hover .bcard__media img{transform:scale(1.03)}
.bcard__ph{display:flex;align-items:center;justify-content:center;height:200px;background:var(--hi);font-family:var(--serif);font-weight:900;font-size:64px;color:var(--ink)}
.bcard__body{padding:22px 24px 24px;display:flex;flex-direction:column;gap:12px;flex:1}
.bcard__meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px}
.bcard__cat{background:var(--hi);color:var(--ink);border:var(--frame) solid var(--ink);font-family:var(--sans);font-size:var(--fs-2xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;padding:1px 8px;line-height:1.5}
.bcard__date{font-family:var(--sans);font-size:var(--fs-sm);font-weight:600;color:var(--mute);letter-spacing:.02em}
.bcard__title{font-size:22px;line-height:1.15}
.bcard__title a:hover{background:var(--hi);box-decoration-break:clone;-webkit-box-decoration-break:clone}
.bcard__excerpt{color:var(--soft);font-size:var(--fs-base)}
/* Separator on the soft nav hairline, not the 2px ink rule — it divides the
   card's own content, it is not a frame edge. */
.bcard__foot{margin-top:auto;border-top:1px solid var(--nav-line);padding-top:14px;display:flex;align-items:center;justify-content:space-between;gap:12px}
/* Underline on --hi, the site's link colour (.tlink, .prose a, .tax-guides__lbl
   and every hover use it). This rule was one of only two anywhere still
   underlining on --hi-2, which reads as a second, darker blue. */
.bcard__more{font-family:var(--sans);font-size:var(--fs-sm);font-weight:800;letter-spacing:.06em;text-transform:uppercase;border-bottom:var(--frame) solid var(--hi);padding-bottom:1px;white-space:nowrap}
.bcard__more:hover{color:var(--hi-2)}
.bcard__read{font-family:var(--sans);font-size:var(--fs-sm);font-weight:600;color:var(--mute);white-space:nowrap}

/* ---- ranked item (comparison) ---- */
.item{display:grid;grid-template-columns:140px 1fr;gap:36px;padding:48px 0;border-bottom:var(--frame) solid var(--line)}
.item:first-of-type{border-top:var(--frame) solid var(--line)}
.sec__head + .item{border-top:0} /* the sec head already rules the top — no double line */
.item__no{font-family:var(--serif);font-size:96px;font-weight:900;line-height:.8;letter-spacing:-0.04em}
/* Award label above product title in comparison ranked items */
.item__award{display:inline-block;background:var(--hi);color:var(--ink);border:var(--frame) solid var(--ink);font-family:var(--sans);font-size:var(--fs-2xs);font-weight:800;letter-spacing:.12em;text-transform:uppercase;line-height:1.4;padding:4px 10px;margin-bottom:10px}
/* 320/48 = .with-aside's exact track/gap, so the rail is the SAME width as the
   product page's sidebar (2026-08-13 normalization). With rank numbers off
   (.item--nonum, the running configuration) the narrative column lands on
   728px — also exactly the product page's main column. */
.item__body{display:grid;grid-template-columns:1fr 320px;gap:48px}
/* German product names are single compound tokens — "Sleepsy Solara
   Tonnentaschenfederkernmatratze" is one 30-character word. A grid track's
   default min-width:auto floors it at min-content, so on a 345px phone that
   one word set a 408px floor for the WHOLE ranked item and dragged the photo,
   the pros/cons and the buy rail out of the viewport with it. Two halves:
   let the word break (hyphens:auto reads the <html lang="de">), and let the
   tracks shrink so no future long token can push the layout around again. */
.item__body,.item__main,.item__head,.right{min-width:0}
.item h3,.item .brand,.item__award{overflow-wrap:break-word;hyphens:auto}
.item .brand{font-weight:700;font-size:var(--fs-sm);letter-spacing:.1em;text-transform:uppercase;color:var(--mute)}
.item h3{font-size:38px;margin:6px 0 14px}
.item h3 .it{font-style:italic;font-weight:600}
.item image-slot{border:var(--frame) solid var(--line);display:block}
.stamp{display:inline-block;background:var(--ink);color:#fff;font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:6px 12px;margin-bottom:16px;white-space:nowrap}
.item .desc{font-size:var(--fs-2xl);color:var(--soft);max-width:52ch;margin-bottom:22px}
/* Customizer > Comparison layout > "intro and product descriptions span the
   full width": drops the reading-measure cap on the comparison hero intro and
   the ranked-item description. Set on both the .hero__lead wrapper and the
   paragraph inside it, since the wrapper's own 54ch would bound the p anyway.
   Specificity has to clear `.item .desc` (0,2,0), hence the descendant form. */
.hero__lead.txt-wide,.item .desc.txt-wide{max-width:none}
.proscons{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.pc{border:var(--frame) solid var(--line);padding:16px 20px;background:var(--card)}
.pc.pos{border-color:var(--pos)} .pc.neg{border-color:var(--neg)}
.pc h5{font-family:var(--sans);font-size:var(--fs-2xs);font-weight:700;letter-spacing:.12em;text-transform:uppercase;margin:0 0 12px;display:flex;align-items:center;gap:8px;padding-bottom:10px;border-bottom:var(--frame) solid var(--hair)}
.pc.pos h5{color:var(--pos)} .pc.neg h5{color:var(--neg)}
.pc h5::before{content:"";width:14px;height:14px;border-radius:50%;display:inline-block;flex:none}
.pc.pos h5::before{background:var(--pos)} .pc.neg h5::before{background:var(--neg)}
.pc ul{list-style:none;margin:0;padding:0;font-size:var(--fs-base);color:var(--soft);display:flex;flex-direction:column;gap:9px}
.pc li{display:flex;gap:10px;align-items:flex-start;line-height:1.4}
.pc li::before{font-weight:800;flex:none;font-size:var(--fs-lg);line-height:1.3}
.pc.pos li::before{content:"+";color:var(--pos)} .pc.neg li::before{content:"\2013";color:var(--neg)}
.right{border-left:var(--frame) solid var(--line);padding-left:28px;display:flex;flex-direction:column}
/* Ranked-item buy rail: follows ITS product down the page and lets go when the
   next item takes over. position:sticky is bounded by its containing block, so
   the travel is exactly .right's box (= the item's body) — no JS, and one item's
   price can never hang over the next item's photo. The measured slack is
   400-850px per item, so it genuinely travels.
   The WRAPPER sticks, not .right itself: .right stretches to draw the divider
   beside the whole narrative, and a sticky .right would collapse to its content
   and drag that line down the page with it. */
.right__stick{display:flex;flex-direction:column;position:sticky;top:calc(var(--nav-h) + var(--subnav-h) + 20px)}
/* HEIGHT GUARD — same reasoning as .aside-wrap's: a sticky block taller than
   the room the chrome leaves it pins its own overflow permanently off-screen,
   and nothing can scroll it back. A rail with a coupon plus the brand block
   measures ~730px, which does not fit a 768px laptop. Cap it and let the
   surplus scroll inside. The padding/margin pair is net-zero for layout and
   keeps borders off the clip edge. Only above the stacking breakpoint. */
@media (min-width: 881px){
  .right__stick{
    max-height:calc(100vh - var(--nav-h) - var(--subnav-h) - 40px);
    overflow-y:auto;
    overscroll-behavior:contain;
    padding:0 6px 6px 0;
    margin:0 -6px -6px 0;
    scrollbar-width:thin;
    scrollbar-color:var(--ink) var(--hair);
  }
}
/* line-height .85 let the 86px serif digits (comma of "9,0") bleed ~13px
   past the line box into the verdict badge below — same overflow family as
   the pcard score fix. .95 keeps the tight look; the badge gets extra air. */
.bigscore{font-family:var(--serif);font-size:86px;font-weight:900;line-height:1;letter-spacing:-0.04em}
.bigscore sup{font-size:var(--fs-4xl);font-weight:600;color:var(--mute);vertical-align:super}
.verdict{display:inline-block;background:var(--hi);color:var(--ink);font-weight:700;font-size:var(--fs-md);padding:4px 10px;margin-top:18px;align-self:flex-start}
/* (.scoreline, the comparison rail's own score row, retired 2026-08-13: the
   rail adopted the product page's .buyrail__score/.buyrail__num/.buyrail__badge
   verbatim so the two rails cannot drift.) */
.price{font-family:var(--serif);font-weight:800;font-size:var(--fs-5xl);margin-top:24px}
.price small{display:block;font-family:var(--sans);font-size:var(--fs-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
/* Struck-through regular price beside an active promo (sm_price_html()) —
   muted and smaller so the promo price stays the loud element. */
.price-was{font-family:var(--sans);font-weight:600;font-size:.6em;color:var(--mute);text-decoration-thickness:1px;margin-left:.3em;white-space:nowrap}
.right .btn{margin-top:18px}

/* ---- table ----
   UNBORDERED BY DEFAULT (2026-08-09). Every table on the site used to sit in a
   2px ink box with a 3px header rule, while its row dividers were drawn with
   --frame-hair/--hair — both zeroed by the live token overrides, so the only
   thing left doing any lining was the heavy outer frame. Now: no outer frame,
   no verticals, and horizontal dividers only, in the nav's light hairline
   (--nav-line, a literal rgba no token override can erase). */
table{border-collapse:collapse}
table th,table td{border:0;border-bottom:1px solid var(--nav-line)}
/* tbody, not just tr: a thead's single row is ALSO a :last-child, so the bare
   selector silently erased the header rule on every .tbl. */
table tbody tr:last-child th,table tbody tr:last-child td{border-bottom:0}
.tbl-scroll{overflow-x:auto}
.tbl{width:100%;border-collapse:collapse;font-size:var(--fs-base)}
.tbl-scroll .tbl{min-width:760px}
/* Article tables must fit the 760px prose column — no horizontal scroll */
/* inside running text the table needs its own bottom gap — paragraphs carry
   margins, the bare .tbl-scroll div doesn't */
.prose .tbl-scroll{margin:0 0 22px}

/* ---- newsletter subscription form (/newsletter/, Newsletter plugin) —
        THE action on that page: big Stark card, oversized fields ---- */
.tnp-subscription{max-width:560px;margin:10px 0 14px;background:var(--card);border:var(--frame-bold) solid var(--line);box-shadow:var(--shadow-6);padding:26px 26px 24px}
.tnp-subscription form{margin:0}
.tnp-subscription .tnp-field{margin:0 0 16px}
.tnp-subscription .tnp-field-email>label{display:block;font-size:var(--fs-2xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--mute);margin-bottom:7px}
.tnp-subscription input.tnp-email{width:100%;height:54px;border:var(--frame) solid var(--line);background:var(--paper);padding:0 16px;font-family:var(--sans);font-size:var(--fs-lg);color:var(--ink)}
.tnp-subscription input.tnp-email:focus{outline:none;border-color:var(--ink)}
.tnp-subscription label{font-size:var(--fs-md);font-weight:600;color:var(--soft);line-height:1.45}
.tnp-subscription input.tnp-privacy{margin-right:8px}
.tnp-subscription .tnp-field-button{margin-bottom:0}
/* !important: the plugin ships its own stylesheet with equal-specificity
   input[type=submit] rules that load after ours */
.tnp-subscription input.tnp-submit{width:100%!important;height:54px!important;background:var(--hi)!important;border:var(--frame) solid var(--line)!important;border-radius:var(--radius)!important;font-family:var(--sans)!important;font-weight:800;font-size:var(--fs-base)!important;letter-spacing:.05em;text-transform:uppercase;color:var(--ink)!important;cursor:pointer;box-shadow:var(--shadow-3);transition:transform .12s,box-shadow .12s,background .12s}
.tnp-subscription input.tnp-submit:hover{background:var(--ink)!important;color:#fff!important;transform:translate(1px,1px);box-shadow:var(--shadow-1)}
.tnp-subscription input.tnp-email{border-radius:var(--radius)!important}
@media (max-width: 620px){.tnp-subscription{padding:20px 18px 18px;box-shadow:var(--shadow-5)}}

/* ---- comparison TOC, static variant (Customizer > Comparison layout) ---- */
.toc-static{background:var(--card);border:2px solid var(--line);box-shadow:6px 6px 0 var(--ink);padding:18px 22px;margin:26px 0 4px}
.toc-static__h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.12em;color:var(--ink);margin:0 0 8px}
.toc-static a{display:flex;align-items:center;gap:12px;padding:9px 0;border-bottom:1px solid var(--hair);color:var(--ink)}
.toc-static a:last-of-type{border-bottom:0;padding-bottom:2px}
.toc-static__no{font-family:var(--serif);font-weight:900;font-size:20px;min-width:26px;color:var(--hi-2);flex:none}
.toc-static__t{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.toc-static__t strong{font-size:14px}
.toc-static__t small{font-size:12px;color:var(--soft)}
.toc-static a:hover .toc-static__t strong{box-shadow:inset 0 -2px var(--hi)}

/* ---- comparison TOC, light variant: no box, arrows instead of numbers,
        only the top pick keeps its rank ---- */
.toc-light{margin:26px 0 4px;max-width:620px}
.toc-light__h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.12em;color:var(--ink);margin:0 0 8px}
.toc-light a{display:flex;align-items:baseline;gap:10px;padding:6px 0;color:var(--ink)}
.toc-light a i{font-style:normal;font-weight:800;color:var(--hi-2);flex:none;width:22px}
.toc-light__no{font-size:15px;color:var(--hi-2);flex:none;width:22px;text-align:left}
.toc-light__win{display:inline-block;font-style:normal;font-family:var(--sans);font-size:10px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;background:var(--hi);color:var(--ink);padding:2px 8px;margin-left:8px;vertical-align:2px;white-space:nowrap}
.toc-light__t{display:flex;flex-direction:column;line-height:1.35;min-width:0}
.toc-light__t strong{font-size:15px}
.toc-light__t small{font-size:12.5px;color:var(--soft)}
.toc-light a:hover .toc-light__t strong{box-shadow:inset 0 -2px var(--hi)}
/* TOC variant D: top 3 visible, remainder in a native <details> disclosure.
   The revealed links reuse the .toc-light a rules above, so the open state is
   identical to variant C.
   NOTE: the FAQ accordion's `details` / `summary` rules used to be bare element
   selectors that reached any <details> on the page, which is why this block
   resets them. They are scoped to .sec--faq now and a TOC never sits inside a
   FAQ section, so those resets are belt-and-braces rather than load-bearing.
   The positive styling below (inline summary, sans 14px, underline) is still
   required — it is what makes a "show more" link look like a link. */
.toc-light .toc-light__more{margin:0;border:0}
.toc-light .toc-light__more>summary{display:inline;cursor:pointer;list-style:none;padding:8px 0 2px;font-family:var(--sans);font-size:14px;font-weight:400;color:var(--ink);text-decoration:underline}
.toc-light .toc-light__more>summary::-webkit-details-marker{display:none}
.toc-light .toc-light__more>summary::after{content:none}
.toc-light .toc-light__more>summary:hover{text-decoration:none}
.toc-light .toc-light__more>summary:focus:not(:focus-visible){outline:none}
.toc-light .toc-light__more .is-open{display:none}
.toc-light .toc-light__more[open] .is-shut{display:none}
.toc-light .toc-light__more[open] .is-open{display:inline}

/* anchor targets clear the sticky chrome (nav + optional subnav) */
.item[id],section[id]{scroll-margin-top:calc(var(--nav-h) + 48px)}
/* Article body headings (single.php injects/keeps the ids, the offset is ours).
   --nav-h is republished from the nav's measured height, so this follows the
   classic 70px bar and the stacked 134px one alike; a fixed value can only be
   right for one of them. 24px keeps a hair of air above the heading. */
.prose h2[id]{scroll-margin-top:calc(var(--nav-h) + var(--subnav-h) + 24px)}

/* ---- comparison sections breathe less on mobile (78+64px gaps read as
        holes between Ranking / Überblick / Aktionen on a phone) ---- */
@media (max-width: 760px){
  .single-sm_comparison main .sec{padding-top:36px;padding-bottom:36px;margin-bottom:28px!important}
}
.prose .tbl-scroll .tbl{min-width:0;font-size:var(--fs-md)}
.prose .tbl td{padding:12px 10px;vertical-align:top}
.prose .tbl th{padding:11px 10px}
.tbl-scroll .spectable{min-width:0} /* 2-col spec tables never need forced width */
.tbl th{font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;text-align:left;padding:13px 13px;border-bottom:1px solid var(--nav-line);background:var(--paper)}
/* sticky column headers only inside scrollable comparison tables — a bare
   position:sticky on every th made sidebar row-headers ride out of the table */
.tbl-scroll thead th{position:sticky;top:0}
.tbl td{padding:15px 13px;border-bottom:1px solid var(--nav-line);vertical-align:middle}
/* Row hover belongs to the SCROLLABLE comparison/deal tables, where it helps
   you keep your place across a wide row. The product page's 2-column
   Technische Daten table (.spectable) never scrolls and must not take the
   sky-fill — design_handoff_produktseite §5. */
.tbl-scroll .tbl:not(.spectable) tr:hover td{background:var(--hi)}
.tbl tr.is-winner td{background:var(--tint-hi)}
.tbl tr.is-winner:hover td{background:var(--hi)}
.tbl .n{font-family:var(--serif);font-weight:800;font-size:var(--fs-3xl)}
.tbl .rank{font-family:var(--serif);font-weight:900;font-size:22px;width:34px;text-align:center}
.tbl .pr{font-weight:700}.tbl .pr small{display:block;font-weight:400;color:var(--mute);font-size:var(--fs-xs)}
.tbl .prod{display:flex;align-items:center;gap:13px;min-width:200px}
.tbl .prod image-slot{width:56px;height:44px;border:var(--frame) solid var(--line);flex:none;display:block}
.tbl .prod b{font-weight:700;display:block;line-height:1.15}
.tbl .prod small{color:var(--mute);font-size:var(--fs-xs);font-weight:600}

/* ---- certification chips + inline links (registry-driven, SM Cockpit) ---- */
/* Boxless: the chip frame competed with the sidecard's own border for the same
   few certs. Wider gap replaces the removed padding as the only separator now
   that nothing encloses each cert. */
.certchips{display:flex;flex-wrap:wrap;gap:14px;align-items:center}
.certchip{display:inline-flex;align-items:center;gap:7px;font-family:var(--sans);font-weight:700;font-size:var(--fs-xs);line-height:1;color:var(--ink);text-decoration:none;transition:.12s}
a.certchip:hover{text-decoration:underline}
/* The sidecard td sets overflow-wrap:anywhere so long material prose cannot
   burst the 320px card; on a cert label that splits the name mid-word
   ("OEKO-" / "TEX"). Break at spaces only. The cell has ~109px of room, so
   every current label - longest is "Made in Germany" at ~100px - sits whole
   and this rule only matters if a longer one is ever added. */
.certchip span{overflow-wrap:normal}
.certchip img{display:block;width:auto;height:22px;max-width:70px;object-fit:contain}
.certlink{font-weight:600}
a.certlink{color:var(--ink);box-shadow:inset 0 -2px var(--hi)}
a.certlink:hover{box-shadow:inset 0 -3px var(--ink)}
.tbl .scorechip{display:inline-flex;align-items:baseline;gap:3px;font-family:var(--serif);font-weight:900;font-size:21px;line-height:1}
.tbl .scorechip i{font-family:var(--sans);font-style:normal;font-size:var(--fs-2xs);font-weight:700;color:var(--mute)}
.tbl .price-b{font-family:var(--serif);font-weight:800;font-size:var(--fs-2xl)}
.tbl-foot{font-size:var(--fs-xs);color:var(--mute);font-weight:600;margin-top:12px;letter-spacing:.02em}
.tbl-scroll{overflow-x:auto;scrollbar-width:thin;scrollbar-color:var(--ink) var(--hair)}
.tbl-scroll::-webkit-scrollbar{height:8px}
.tbl-scroll::-webkit-scrollbar-track{background:var(--hair)}
.tbl-scroll::-webkit-scrollbar-thumb{background:var(--ink);border:var(--frame) solid var(--hair)}

/* ---- layout w/ sidebar ---- */
.with-aside{display:grid;grid-template-columns:1fr 320px;gap:48px;align-items:start}
/* stick right below the REAL sticky chrome (nav + optional subnav, both
   JS-measured) — a hardcoded 124px slid under the taller product-page chrome */
/* The rail draws NO frame of its own: it wrapped cards that are already lined
   (.buycard, every .sidecard), which read as two nested borders down the whole
   sidebar and doubled to 4px at the bottom against .sidecard:last-child.
   The section dividers went with it. They never landed where you would draw a
   line by hand: the buycard's bottom rule and the coupon card's top rule fall
   in the SAME place (4px of line under the reassurance list / above Gutschein),
   and with a radius set both curve at the ends, so each divider read as a lens
   floating between two blocks rather than a separator. What separates the
   sections now is their own padding, plus each card's h4 rule. */
.aside{position:sticky;top:calc(var(--nav-h) + var(--subnav-h) + 20px);display:flex;flex-direction:column;gap:0;border:var(--frame-light) solid var(--line);border-top:0}
.aside .buycard{border:0;border-bottom:var(--frame-light) solid var(--line)}
.main-col > section + section{margin-top:52px}
.main-col > section > .kicker{margin-bottom:6px}
.sidecard{padding:22px;border-top:var(--frame-light) solid var(--line);border-bottom:0}
.sidecard:last-child{border-bottom:var(--frame-light) solid var(--line)}
/* Coupon strip: no heading row of its own (the value IS the label), so it
   only needs the vertical air that separates it from the rules above/below. */
.sidecard--coupon{padding:9px 22px}
.sidecard h4{font-family:var(--sans);font-size:var(--fs-xs);font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink);margin:0 0 14px;padding-bottom:11px;border-bottom:var(--frame) solid var(--ink)}
.top3 .t3{display:flex;align-items:center;gap:14px;padding:12px 0;border-bottom:var(--frame-hair) solid var(--hair)}
.top3 .t3:last-child{border-bottom:0}
.top3 .t3 b{font-family:var(--serif);font-weight:900;font-size:28px;line-height:1}
.top3 .t3 small{font-size:var(--fs-3xs);letter-spacing:.1em;text-transform:uppercase;color:var(--hi-2);font-weight:700;display:block}
.top3 .t3 span{font-weight:600;font-size:var(--fs-md)}
.note{font-size:var(--fs-md);color:var(--soft)} .note strong{color:var(--ink)}

/* ---- product page: spec table, buy card, trust ----
   No outer frame around Technische Daten / Auf einen Blick, and the row rules
   are on --nav-line rather than --frame-hair/--hair, which the site overrides
   to 0px/#fff — the label/value list now actually shows its dividers. */
.spectable{width:100%;border-collapse:collapse;border:0}
.spectable th,.spectable td{text-align:left;padding:14px 18px;border-bottom:1px solid var(--nav-line);font-size:var(--fs-base)}
.spectable th{font-family:var(--sans);font-size:var(--fs-md);letter-spacing:0;text-transform:none;color:var(--soft);font-weight:600;width:40%;vertical-align:top}
.spectable td{font-weight:700;color:var(--ink)}
.spectable tr:last-child th,.spectable tr:last-child td{border-bottom:0}
.spectable td .hl{font-weight:800}
/* measured value trailing a spec label ("Mittelfest (6/10)") — secondary to it */
.spectable td .spec__num{font-weight:600;color:var(--soft)}
/* sidebar "Auf einen Blick": fixed layout so the table can never exceed 320px */
.sidecard .spectable{table-layout:fixed}
.sidecard .spectable th{width:42%}
.sidecard .spectable td{overflow-wrap:anywhere}
.sidecard .spectable th,.sidecard .spectable td{padding:10px 12px;font-size:var(--fs-md)}
.buycard{border:var(--frame) solid var(--line);background:var(--card)}
.buycard__score{background:var(--ink);color:#fff;padding:16px 18px;display:flex;align-items:center;gap:14px}
.buycard__score .bigscore{color:#fff;font-size:48px}
/* --on-dark-soft, NOT --mute: this row has a dark --ink fill behind it, and
   --mute (#6c6b64) is a warm mid-grey built for light surfaces — on the navy it
   came out at 2.9:1 and the wrong temperature. The label beside it already uses
   the on-dark family; the "/10" belongs there too. */
.buycard__score .bigscore sup{color:var(--on-dark-soft)}
.buycard__score b{display:block;font-size:var(--fs-base)}
.buycard__score span{font-size:var(--fs-sm);color:var(--on-dark-soft)}
.buycard__body{padding:18px;display:flex;flex-direction:column;gap:11px}
.buycard__price{font-family:var(--serif);font-weight:800;font-size:var(--fs-5xl)}
.buycard__sub{text-align:center;font-size:var(--fs-sm);color:var(--mute);font-weight:600;letter-spacing:.03em}
.trustrow{display:grid;grid-template-columns:repeat(4,1fr);border:var(--frame) solid var(--line)}
.trust{padding:20px 14px;text-align:center;border-right:var(--frame) solid var(--line)}
.trust:last-child{border-right:0}
.trust b{display:block;font-family:var(--serif);font-weight:800;font-size:var(--fs-2xl)}
.trust span{font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;color:var(--mute);font-weight:600}
.byline{display:flex;align-items:center;gap:14px}
.byline__av{width:46px;height:46px;background:var(--hi);font-family:var(--serif);font-weight:900;display:grid;place-items:center;flex:none;border:var(--frame) solid var(--line)}
.byline b{font-size:var(--fs-base);font-weight:700}.byline small{display:block;color:var(--mute);font-size:var(--fs-sm)}

/* ---- subnav ---- */
/* --nav-h is measured by JS (initNavHeight) — the stacked layout's two-row
   header is ~126px tall, so a hardcoded 70px slid the bar under the nav */
.subnav{position:sticky;top:var(--nav-h);z-index:var(--z-subnav);background:var(--paper);border-bottom:var(--frame) solid var(--line)}
.subnav__row{display:flex;gap:0;overflow-x:auto}
.subnav__link{flex:none;padding:10px 14px;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--soft);border-right:var(--frame-hair) solid var(--hair)}
.subnav__link .n{color:var(--hi-2)}
.subnav__link:hover,.subnav__link.is-active{background:var(--ink);color:#fff}
.subnav__link.is-active .n{color:var(--hi)}

/* ---- coupon (discount) ---- */
.coupon{border:var(--frame-bold) solid var(--line);background:var(--card);padding:0;display:grid;grid-template-columns:1fr auto}
.coupon__main{padding:38px}
.coupon__pct{font-family:var(--serif);font-weight:900;font-size:96px;line-height:.8;letter-spacing:-0.04em}
.coupon__pct span{font-size:44px}
.coupon__desc{color:var(--soft);max-width:40ch;margin-top:18px}
.coupon__code{border-left:var(--frame-bold) solid var(--line);background:var(--hi);padding:38px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:14px;min-width:240px}
.coupon__code label{font-size:var(--fs-2xs);font-weight:700;letter-spacing:.14em;text-transform:uppercase}
.codeval{font-family:var(--serif);font-weight:900;font-size:32px;letter-spacing:.06em;border:var(--frame) dashed var(--ink);padding:10px 20px;background:var(--card)}
.codecopy{background:var(--ink);color:#fff;border:0;font-family:var(--sans);font-weight:700;font-size:var(--fs-sm);letter-spacing:.06em;text-transform:uppercase;padding:12px 20px;cursor:pointer}
.codecopy.copied{background:var(--pos)}

/* ---- dealcode: blurred reveal widget (brand discount codes) ---- */
.dealcode{border:var(--frame) dashed var(--ink);background:var(--card);padding:13px 15px;display:flex;align-items:center;gap:13px;flex-wrap:wrap}
.dealcode__pct{font-family:var(--serif);font-weight:900;font-size:25px;line-height:1;flex:none}
.dealcode__txt{min-width:80px}
.dealcode__txt b{display:block;font-size:var(--fs-base);line-height:1.12}
.dealcode__txt small{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.08em;color:var(--mute);font-weight:700}
.dealcode__act{display:flex;align-items:center;gap:10px;margin-left:auto}
.dealcode__code{font-family:var(--sans);font-weight:800;letter-spacing:.18em;font-size:var(--fs-base);border:var(--frame) solid var(--hair);background:var(--paper);padding:9px 16px;transition:color .2s,text-shadow .2s;white-space:nowrap}
/* Locked: the smear says "hidden", the dashed ink edge says "hidden CODE"
   (Tim, 2026-08-23), site-wide — the row cells, the brand rail, the comparison
   tables, every variant. The smear was carrying that job alone in production:
   the box is bordered in --hair, which the live token overrides run at #ffffff
   on a white --card, so the field had no visible edge at all and read as a
   smudge rather than as a redacted code.

   The edge is EXACTLY the revealed state's edge — same declarations, same
   `dashed`, same `var(--ink)` (Tim, 2026-08-23: "just use the exact same
   dotting from when it's revealed"). Two earlier attempts are on the record so
   nobody re-treads them: `border-style:dotted` is a dense stipple at 1px
   because CSS ties the dot size and the gap to the border width, and four
   repeating-linear-gradients painting a 2px mark every 24px produced
   rectangular dashes, not dots. Native `dashed` is the only one of the three
   that looks like the rest of the site, and reusing the revealed rule's exact
   values means the box does not change its edge under the click at all — only
   the glyphs resolve. Literal ink, so neither the --hair nor the --frame-hair
   override can erase it.

   Border WIDTH deliberately stays with the variant (--frame on the base box,
   1px on --peek and --cell), exactly as the revealed rule leaves it.

   The blur is a TEXT-SHADOW, not `filter:blur()`: filter blurs everything the
   element paints, border included, so the edge came out as a soft halo and the
   whole point was lost. Blurring the glyphs instead leaves the box crisp.
   Nothing leaks either way — the masked glyphs are bullets, and the real code
   only ever exists in data-code until initReveal() writes it.
   Selector carries .dealcode so it outranks the later per-variant border
   shorthands (.dealcode--peek sets `border:1px dashed var(--hair)`); `locked`
   is removed by initReveal(), so the revealed rule is never in this fight. */
.dealcode .dealcode__code.locked{color:transparent;text-shadow:0 0 6px var(--soft),0 0 11px var(--soft);user-select:none;border-color:var(--ink);border-style:dashed}
.dealcode.revealed .dealcode__code{user-select:all;border-color:var(--ink);border-style:dashed}
.dealcode__btn{background:var(--ink);color:#fff;border:0;font-family:var(--sans);font-weight:700;font-size:var(--fs-xs);letter-spacing:.04em;text-transform:uppercase;padding:11px 14px;cursor:pointer;white-space:nowrap;transition:background .15s}
/* Match the sitewide CTA hover (.btn:hover) — this used to go --hi-2 orange,
   the only CTA on the site that did, and it kept white text on a light fill. */
.dealcode__btn:hover{background:var(--hi);color:var(--ink)}
.dealcode__btn.copied{background:var(--pos)}
.dealcode--block{flex-direction:column;align-items:stretch;gap:9px;text-align:center}
.dealcode--block .dealcode__act{margin-left:0;flex-direction:column;align-items:stretch;gap:9px}
.dealcode--block .dealcode__code{width:100%;text-align:center;box-sizing:border-box}
.dealcode--block .dealcode__btn{width:100%}
.dealcode--block .dealcode__pct{font-size:22px;letter-spacing:-.01em}
/* The comparison rail used --block and it was far too tall there; it now uses
   --slim, like the review page's rail. The rail is ~230px wide, so let the strip
   wrap and keep the button full-width on its own line rather than squeezing it
   beside the value. */
.right__stick .dealcode--slim{gap:9px;padding:12px 14px;justify-content:center}
/* The value gets its own centred line above the full-width button; the button
   needs the box padding to breathe or it reads as if it is touching the frame. */
.right__stick .dealcode--slim .dealcode__pct,.right__stick .dealcode--slim .dealcode__txt{width:100%;justify-content:center;text-align:center}
.right__stick .dealcode--slim .dealcode__act{margin-left:0;width:100%}
.right__stick .dealcode--slim .dealcode__btn{flex:1 1 auto;text-align:center;padding:9px 12px}
/* Certification logos in the rail (registry-driven, see sm_render_cert_chips).
   Typographically part of the spec list above it, not a section of its own: same
   13px sentence-case line as "Garantie", then the logos. */
.rail-certs{display:block;margin-top:13px}
.rail-certs--solo{margin-top:var(--sp-4);font-size:13px}
.rail-certs__lbl{display:block;font-size:13px;font-weight:400;letter-spacing:0;text-transform:none;color:var(--ink);margin-bottom:8px}
.rail-certs .certchips{gap:12px 16px}
.rail-certs .certchip{position:relative}
/* Logos carry the meaning on their own (the wordmark beside them is hidden
   below), so they get to be read at a glance rather than as a footnote. */
.rail-certs .certchip img{height:37.5px;max-width:120px}
/* The logo IS the wordmark — showing the label beside it read as a stutter in a
   230px rail. Kept for assistive tech (and for certs with no logo, which still
   render their label as text). Mirrors .screen-reader-text.
   GENERAL sibling (~), not adjacent (+): the lazy-load plugin injects a
   <noscript> fallback between the <img> and the label, so `img + span` matches
   nothing on the live page. */
.rail-certs .certchip img ~ span{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* Cell (comparison reduction table): the container carries NO edge of its own.
   .dealcode's base `border:var(--frame) dashed var(--ink)` plus production's
   --radius override (the stylesheet ships --radius:0; the live Design-Tokens
   page runs 20px) drew a rounded dashed pill AROUND a chip whose code box is
   already dashed — two nested outlines, one of them a lozenge, for one hidden
   code (Tim, 2026-08-23). Stripped to nothing, so the only edge left is the
   global locked-code treatment on .dealcode__code itself, same as everywhere
   else. --peek opts out of the container edge the same way.

   The `border-radius:0` here is intent, not a proven override: the container
   still computes 20px in production after this rule ships, and enumerating
   every accessible stylesheet rule that declares a radius property found none
   that matches this element — so whatever applies it is not reachable through
   CSSOM (LiteSpeed's optimiser and the live Design-Tokens override are the
   suspects). Harmless as long as there is no border and no background to
   curve, which is the state above. Do not delete the declaration to "clean
   up": it is what keeps a re-added border from coming back as a lozenge. */
.dealcode--cell{display:inline-flex;align-items:center;gap:7px;padding:0;border:0;border-radius:0;background:none;flex-wrap:nowrap;vertical-align:middle}
.dealcode--cell .dealcode__code{font-size:var(--fs-xs);letter-spacing:.1em;padding:5px 10px;border-width:1px}
.dealcode--cell .dealcode__btn{font-size:var(--fs-2xs);padding:7px 10px;letter-spacing:.03em}
/* Slim: one row, for the STICKY product sidebar. The stacked --block version
   ran ~230px tall there, a third of the whole rail, and pushed the cards under
   it below the fold on a 768px laptop. Same dashed frame and same value-first
   reading, ~90px instead. The masked bullets are the one thing dropped: they
   are aria-hidden decoration and the button already says a code is hidden, so
   they buy nothing at the price of a second row. */
.dealcode--slim{align-items:center;gap:5px 10px;padding:5px 9px}
.dealcode--slim .dealcode__pct{font-size:var(--fs-base);letter-spacing:0}
/* Both captions sit ON their line, never under it. Stacking them was what made
   the strip tall: on a coupon with no % to show, the b+small "Gutschein /
   ALLNATURA" pair alone was 42px of the 68px strip. */
.dealcode--slim .dealcode__pct small{display:inline;margin:0 0 0 5px;font-size:var(--fs-3xs);letter-spacing:.08em;color:var(--mute)}
.dealcode--slim .dealcode__txt{min-width:0;display:flex;align-items:baseline;gap:6px;flex-wrap:nowrap}
.dealcode--slim .dealcode__txt b{display:inline;font-size:var(--fs-sm);line-height:1}
.dealcode--slim .dealcode__txt small{font-size:var(--fs-3xs);letter-spacing:.07em;white-space:nowrap}
.dealcode--slim .dealcode__btn{font-size:var(--fs-3xs);padding:6px 9px;letter-spacing:.03em}
.dealcode--slim .dealcode__code{font-size:var(--fs-xs);letter-spacing:.1em;padding:4px 8px;border-width:1px}
.dealcode--slim:not(.revealed) .dealcode__code{display:none}
/* Revealed, the code IS the message, so the label steps aside and the code
   takes its room on the same line. Keeps the strip at one line in both states
   (59px locked, 62px revealed) instead of reflowing the rail under the click. */
.dealcode--slim.revealed .dealcode__pct small{display:none}
.dealcode--slim.revealed .dealcode__txt{display:none}

/* ---- live deal news (brand-associated, updates daily) ---- */
@keyframes sm-livepulse{0%{transform:scale(1);opacity:1}70%{transform:scale(2.6);opacity:0}100%{opacity:0}}
/* No frame and no offset shadow: the --hi fill already reads as a banner, and
   the 2px + 4px pair was two of the heaviest lines on three templates. The
   shadow goes with the border on purpose — a hard ink offset with nothing to
   cast it reads as a rendering fault, not as depth. */
.dealnow{position:relative;display:flex;align-items:center;gap:16px;border:var(--frame-light) solid var(--ink);background:var(--hi);padding:13px 17px;box-shadow:none}
.dealnow__live{display:inline-flex;align-items:center;gap:8px;font-family:var(--sans);font-weight:800;font-size:var(--fs-2xs);letter-spacing:.08em;text-transform:uppercase;color:var(--ink);flex:none}
.dealnow__dot{position:relative;width:9px;height:9px;border-radius:50%;background:var(--neg);flex:none}
.dealnow__dot::after{content:"";position:absolute;inset:0;border-radius:50%;background:var(--neg);animation:sm-livepulse 1.8s ease-out infinite}
.dealnow__txt{flex:1;font-weight:600;font-size:var(--fs-lg);line-height:1.35;color:var(--ink)}
.dealnow__txt b{font-weight:800}
.dealnow__meta{flex:none;font-family:var(--sans);font-size:var(--fs-2xs);font-weight:700;letter-spacing:.02em;color:var(--ink);opacity:.62}
.dealnow__cta{flex:none}
.dealnow--gap{margin-bottom:34px} /* was an inline style on the brand/discount bars */
.dealnow--inline{box-shadow:none;padding:11px 14px;gap:12px;margin:18px 0 28px}
/* Leading its section, the ticker's top margin only adds to the section's own
   padding — the gap above it should read like the 28px gap below it. */
.dealnow--inline:first-child{margin-top:0}
.dealnow--inline .dealnow__txt{font-size:var(--fs-base)}
@media (prefers-reduced-motion:reduce){.dealnow__dot::after{animation:none}}
@media (max-width: 620px){.dealnow{flex-wrap:wrap;gap:10px}.dealnow__meta{display:none}.dealnow__cta{width:100%}.dealnow__cta .btn{width:100%}}

/* ---- deal row (brand rabatt page) ---- */
.deal{border:var(--frame) solid var(--line);background:var(--card);display:grid;grid-template-columns:118px minmax(0,1fr) 320px;align-items:stretch;margin-bottom:18px}
.deal--expired{opacity:.55}
.deal__pct{background:var(--dark);color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-family:var(--serif);font-weight:900;font-size:var(--fs-5xl);line-height:.9;text-align:center;padding:18px 10px}
.deal__pct small{font-family:var(--sans);font-size:var(--fs-3xs);font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--hi)}
.deal__main{padding:22px 24px;min-width:0;overflow-wrap:break-word}
.deal__main h3{font-size:21px;line-height:1.1}
.deal__main p{color:var(--soft);font-size:var(--fs-base);margin-top:7px}
.deal__meta{margin-top:14px;font-size:var(--fs-md);font-weight:600;letter-spacing:0;color:var(--soft);display:flex;gap:8px 18px;flex-wrap:wrap;align-items:center}
.deal__meta .ok{color:var(--pos);font-weight:700;display:inline-flex;align-items:center;gap:6px}
.deal__meta .ok::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--pos)}
.deal__act{border-left:var(--frame) solid var(--line);padding:20px 22px;display:flex;flex-direction:column;justify-content:center;gap:10px}
@media (max-width: 760px){.deal{grid-template-columns:1fr}.deal__pct{flex-direction:row;gap:10px;font-size:var(--fs-4xl);padding:14px}.deal__act{border-left:0;border-top:var(--frame) solid var(--line)}}

/* ---- faq ----
   SCOPED TO .sec--faq ON PURPOSE (2026-08). These were bare `details` /
   `summary` element selectors, which meant the FAQ accordion styled every
   disclosure widget on the site. That is not theoretical: `details:first-of-type`
   is (0,1,1) and beat `.item__more`'s own border shorthand at (0,1,0), so the
   "Mehr Produktdaten" box on comparison pages lost its top edge and read as
   broken; the same rule overrode the deal disclosures' explicit `border-top:0`.
   Both were patched individually, then the patches were deleted in favour of
   fixing the cause here.

   sm_render_faq() puts .sec--faq on the section, and it is the ONLY thing that
   emits a bare <details> on the front end — verified across all templates and
   the whole database (0 posts and 0 meta values contain "<details>"). So this
   scope costs nothing and closes the leak permanently: a new disclosure
   component can never inherit FAQ styling by accident again.

   The separators themselves are the hairline the tables and the light nav draw
   (1px + --nav-line, a literal rgba no token override can erase); the accordion
   used to line every row with the 2px ink frame, which read as a stack of heavy
   rules against the section heading above it. The heading keeps its own
   --frame-bold rule (.sec__head): that one IS meant to be heavy. */
.sec--faq details{border-bottom:1px solid var(--nav-line)}
.sec--faq details:first-of-type{border-top:1px solid var(--nav-line)}
/* directly under a section heading the top bar doubles the heading rule */
.sec--faq .sec__head + details{border-top:0}
.sec--faq summary{list-style:none;cursor:pointer;padding:26px 0;font-family:var(--serif);font-weight:700;font-size:var(--fs-4xl);display:flex;justify-content:space-between;gap:18px}
.sec--faq summary::-webkit-details-marker{display:none}
.sec--faq summary::after{content:"＋";font-family:var(--sans);color:var(--hi-2)}
.sec--faq details[open] summary::after{content:"−"}
.sec--faq details p{color:var(--soft);padding:0 0 26px;max-width:70ch;font-size:var(--fs-xl)}

/* ---- footer ---- */
.foot{background:var(--dark);color:var(--on-dark-soft);padding:64px 0 32px}
.foot__grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1.2fr;gap:40px}
.foot .logo{color:#fff;margin-bottom:16px;display:inline-block}
.foot p{font-size:var(--fs-md);max-width:42ch;line-height:1.6}
.foot__disc{font-size:var(--fs-xs);color:var(--on-dark-mute);margin-top:14px}
.foot h5{font-family:var(--sans);font-size:var(--fs-2xs);letter-spacing:.14em;text-transform:uppercase;color:var(--on-dark-mute);font-weight:700;margin:0 0 16px}
.foot ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:11px}
.foot ul a{font-size:var(--fs-base);color:var(--on-dark-soft)}.foot ul a:hover{color:var(--on-dark);box-shadow:inset 0 -2px var(--hi)}
.foot__bottom{margin-top:48px;padding-top:24px;border-top:var(--frame-hair) solid var(--on-dark-line);display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;font-size:var(--fs-xs);letter-spacing:.04em;color:var(--on-dark-mute);text-transform:uppercase;font-weight:600}

/* ---- theme panel ---- */
.sm-btn{position:fixed;right:22px;bottom:22px;z-index:var(--z-overlay);display:inline-flex;align-items:center;gap:9px;padding:13px 18px;border:var(--frame) solid var(--ink);background:var(--hi);color:var(--ink);cursor:pointer;font-family:var(--sans);font-weight:700;font-size:var(--fs-md);box-shadow:var(--shadow-5)}
.sm-btn:hover{transform:translate(2px,2px);box-shadow:var(--shadow-2)}
.sm-panel{position:fixed;right:22px;bottom:80px;z-index:calc(var(--z-overlay) + 1);width:320px;max-width:calc(100vw - 44px);background:var(--card);border:var(--frame) solid var(--ink);box-shadow:var(--shadow-6);padding:22px;max-height:calc(100vh - 120px);overflow:auto}
.sm-panel[hidden]{display:none}
.sm-panel__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}
.sm-panel__head b{font-family:var(--serif);font-size:22px;font-weight:900}
.sm-panel__head button{background:none;border:0;font-size:var(--fs-4xl);line-height:1;cursor:pointer;color:var(--ink)}
.sm-grp{margin-bottom:20px}
.sm-grp > label{display:block;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);margin-bottom:10px}
.sm-sw{display:flex;gap:10px}
.sm-sw button{width:34px;height:34px;border:var(--frame) solid var(--ink);cursor:pointer;padding:0}
.sm-sw button[aria-pressed="true"]{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink)}
.sm-seg{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;border:var(--frame) solid var(--ink)}
.sm-seg button{background:var(--card);border:0;border-right:var(--frame) solid var(--ink);padding:10px 6px;font-family:var(--sans);font-weight:700;font-size:var(--fs-sm);cursor:pointer;color:var(--soft)}
.sm-seg button:last-child{border-right:0}
.sm-seg button[aria-pressed="true"]{background:var(--ink);color:#fff}
.sm-reset{width:100%;background:none;border:var(--frame) solid var(--ink);padding:10px;font-family:var(--sans);font-weight:700;font-size:var(--fs-sm);cursor:pointer}
.sm-reset:hover{background:var(--hi)}
@media print{.sm-btn,.sm-panel{display:none!important}}

/* ---- mega menu (giga nav) ---- */
/* keep header sticky (sticky also anchors the absolute .mega-wrap) */
.nav{position:sticky;top:0;z-index:var(--z-nav)}
body.admin-bar .nav{top:32px}
@media screen and (max-width: 782px){body.admin-bar .nav{top:46px}}
.nav__menu{display:flex;align-items:center;gap:2px}
.nav__menu > a,.megabtn{font-family:var(--sans);font-weight:700;font-size:var(--fs-base);color:var(--ink);background:none;border:0;cursor:pointer;padding:24px 15px;display:inline-flex;align-items:center;gap:7px}
.nav__menu > a:hover,.megabtn:hover,.has-mega.is-open .megabtn{color:var(--ink)}
/* underline marks the CURRENT PAGE only — the open/hover state keeps just
   the background swap (the accent underline on hover read as noise) */
.nav__menu a.is-active{box-shadow:inset 0 -3px var(--hi)}
.megabtn .chev{font-size:var(--fs-3xs);transition:transform .18s}
.has-mega.is-open .megabtn .chev{transform:rotate(180deg)}
.mega-wrap{position:absolute;left:0;right:0;top:100%;background:var(--paper);border-top:var(--frame-bold) solid var(--ink);border-bottom:var(--frame-bold) solid var(--ink);box-shadow:0 30px 50px rgba(0,0,0,.18);display:none;z-index:var(--z-panel)}
.mega-wrap.is-open{display:block}
.mega-wrap > .wrap{padding-top:22px;padding-bottom:22px}

.mega-arts{display:flex;flex-direction:column}
.mega-art{display:flex;gap:14px;align-items:center;padding:12px 0;border-bottom:var(--frame-hair) solid var(--hair)}
.mega-art:last-child{border-bottom:0}
.mega-art__img{width:74px;height:54px;border:var(--frame) solid var(--ink);flex:none;overflow:hidden;background:var(--hair)}
.mega-art__img image-slot,.mega-art__img img{display:block;width:100%;height:100%;object-fit:cover}
.mega-art__t b{font-family:var(--sans);font-weight:700;font-size:var(--fs-base);line-height:1.3;color:var(--ink);display:block}
.mega-art__t small{font-size:var(--fs-2xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
.mega-art:hover .mega-art__t b{box-shadow:inset 0 -2px var(--hi)}

.mega{display:grid;grid-template-columns:1fr 320px;gap:0;border:var(--frame) solid var(--line);background:var(--card)}
.mega-cats{display:grid;grid-template-columns:repeat(3,1fr)}
.mega-cats .mega-col:last-child{border-right:0}
/* Rabatte panel: one shared header, then two sub-columns split by a slim
   hair divider ("Nach Kategorie" / "Marken-Gutscheine" — see design). */
.mega-split{display:grid;grid-template-columns:1fr 1fr}
.mega-split__col{min-width:0;display:flex;flex-direction:column;padding-right:24px;border-right:var(--frame) solid var(--hair)}
.mega-split__col+.mega-split__col{padding-right:0;padding-left:24px;border-right:0}
.mega-kicker{font-family:var(--sans);font-size:var(--fs-xs);font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--blue);margin:0 0 8px} /* design Sleep Blue kicker */
.mega-catdeal{display:flex;align-items:center;gap:11px;padding:10px 8px;margin:0 -8px;color:var(--ink);border-bottom:var(--frame-hair) solid var(--hair)}
.mega-catdeal:hover b{box-shadow:inset 0 -2px var(--hi)}
.mega-catdeal__dot{width:9px;height:9px;border-radius:50%;background:var(--neg);flex:none}
.mega-catdeal b{font-size:var(--fs-base);font-weight:600;min-width:0}
.mega-catdeal__n{margin-left:auto;font-size:var(--fs-sm);color:var(--mute);white-space:nowrap;flex:none}
.mega-catdeal:has(+ .mega-link--all){border-bottom:0}
/* Category panel: the same split, but its two columns feed ONE shared
   .mega-foot underneath instead of a per-column "all" link. The last row of a
   column therefore has to drop its hairline — it would otherwise double up
   with the foot's top border (same reason as the .mega-link rule below). */
.mega-split--cat .mega-split__col > .mega-link:last-child{border-bottom:0}
/* A short column must not shrink the divider: both stretch to the tallest. */
.mega-split--cat .mega-split__col{align-self:stretch}
.mega-col{padding:22px 24px;border-right:var(--frame) solid var(--hair);min-width:0}
/* Column heading rule (ABOVE the link list) — off. Same for the rules BELOW the
   list (.mega-link--all, .mega-foot, .mega-cards). The per-row hairlines inside
   the list and the frames around the Testberichte/Vergleiche cards stay. */
.mega-col h6{font-family:var(--sans);font-weight:800;font-size:var(--fs-xs);letter-spacing:.12em;text-transform:uppercase;color:var(--ink);margin:0 0 8px;padding:0 0 12px;border:0;border-bottom:var(--frame-light) solid var(--line);display:flex;align-items:center;gap:10px}
/* theme: blog panel left column (replaces handoff inline styles) */
.mega-col__label{border:0;padding:0;font-size:var(--fs-sm);letter-spacing:.1em;text-transform:uppercase;color:var(--mute);font-family:var(--sans);font-weight:700;margin-bottom:6px}
.mega-col h6.mega-col__label{display:block}
.mega-ic{width:26px;height:26px;border:var(--frame) solid var(--ink);color:var(--ink);display:grid;place-items:center;flex:none}
.mega-ic svg{width:15px;height:15px;display:block}
.mega-link{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 10px;margin:0 -10px;font-weight:600;font-size:var(--fs-base);color:var(--ink);border-bottom:var(--frame-hair) solid var(--hair);transition:background .12s,color .12s}
.mega-link i{font-style:normal;color:var(--hi-2);opacity:0;transform:translateX(-5px);transition:.14s;font-weight:800}
.mega-link:hover{background:var(--paper)}
.mega-link:hover i{opacity:1;transform:none}
.mega-link--all{margin-top:auto;color:var(--ink);font-weight:800;border-top:var(--frame-light) solid var(--line);border-bottom:0;padding-top:13px}
/* margin-top:auto opens a gap when the neighboring featured card is taller
   (2-line star titles) — the last link's hairline + the --all top border then
   read as a double line. The --all border alone closes the column. */
.mega-link:has(+ .mega-link--all),.mega-link:has(+ .mega-foot){border-bottom:0}

/* ---- category-panel footer group (all-comparisons + aux reviews/deals) ---- */
/* Style A (default): the bold --all exactly as the panel always closed,
   then ONE compact row of quiet uppercase micro-links beneath it. */
.mega-foot{margin-top:auto;display:flex;flex-direction:column;border-top:var(--frame-light) solid var(--line)}
.mega-foot .mega-link--all{margin-top:0;border-top:0;padding-top:12px}
.mega-foot__aux{display:flex;flex-wrap:wrap;gap:6px 22px;padding:0 10px 10px;margin:-2px -10px 0}
.mega-foot__aux a{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-xs);font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--soft)}
.mega-foot__aux a i{font-style:normal;font-weight:800;color:var(--hi-2)}
.mega-foot__aux a:hover{color:var(--hi-2)}
/* Style B: yellow primary button + two half-width outline chips. */
.mega-foot--b{border-top:0;padding-top:14px}
.mega-foot--b .mega-link--all{margin:0;border:var(--frame) solid var(--ink);background:var(--hi);color:var(--ink);padding:10px 14px;font-size:var(--fs-md);font-weight:800;transition:transform .12s,box-shadow .12s}
.mega-foot--b .mega-link--all:hover{background:var(--hi);transform:translate(-1px,-1px);box-shadow:var(--shadow-2)}
.mega-foot--b .mega-link--all i{opacity:1;transform:none}
.mega-foot--b .mega-foot__aux{display:grid;grid-template-columns:repeat(auto-fit,minmax(0,1fr));gap:8px;padding:8px 0 0;margin:0}
.mega-foot--b .mega-foot__aux a{border:var(--frame) solid var(--line);background:var(--card);padding:9px 12px;font-size:var(--fs-xs);justify-content:space-between;color:var(--ink);transition:transform .12s,box-shadow .12s,background .12s}
.mega-foot--b .mega-foot__aux a:hover{background:var(--paper);transform:translate(-1px,-1px);box-shadow:var(--shadow-line);color:var(--ink)}
.mega-link--all i{opacity:1;color:var(--hi-2)}
.mega-col{display:flex;flex-direction:column}
/* Star product card: no lining at all (left divider, image rule, and the
   .nav--st variant's own frame further down). Its dark fill separates it from
   the panel on its own. */
.mega-feat{display:flex;flex-direction:column;background:var(--dark);color:#fff;text-decoration:none;border-left:var(--frame-light) solid var(--line)}
.mega-feat__img{position:relative;height:158px;border-bottom:var(--frame-light) solid var(--ink);background:var(--dark-alt);overflow:hidden}
.mega-feat__img image-slot,.mega-feat__img img{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:cover}
/* theme: contain-fit for brand logos in the Rabatte feat card (replaces inline style) */
.mega-feat__img img.mega-feat__img--contain{object-fit:contain;background:var(--paper);padding:20px}
.mega-feat__tag{position:absolute;top:12px;left:12px;background:var(--hi);color:var(--ink);font-family:var(--sans);font-size:var(--fs-2xs);font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:5px 10px;z-index:2}
.mega-feat__body{padding:22px;display:flex;flex-direction:column;gap:9px;flex:1}
.mega-feat__body .kicker{color:var(--hi)}
.mega-feat__body h4{color:#fff;font-family:var(--serif);font-weight:800;font-size:21px;line-height:1.12;margin:0}
.mega-feat__row{display:flex;align-items:center;justify-content:space-between;margin-top:auto;gap:14px}
.mega-feat__score{font-family:var(--serif);font-weight:900;font-size:40px;line-height:1;color:#fff}
.mega-feat__score i{font-style:normal;font-family:var(--sans);font-size:var(--fs-xs);font-weight:700;color:var(--on-dark-soft)}
/* theme: smaller score text for long deal strings (replaces inline style) */
.mega-feat__score--sm{font-size:var(--fs-2xl);line-height:1.2}
.megabg{position:fixed;inset:0;z-index:var(--z-megabg);display:none}
.nav.open .megabg{display:block}

/* ---- mobile nav (burger + panel, ≤980px) ---- */
.nav-burger{display:none;flex-direction:column;align-items:center;justify-content:center;gap:5px;width:44px;height:44px;padding:0;background:var(--paper);border:var(--frame) solid var(--ink);cursor:pointer;flex:none}
.nav-burger span{display:block;width:20px;height:2px;background:var(--ink);transition:transform .18s,opacity .18s}
.nav-burger.is-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav-burger.is-open span:nth-child(2){opacity:0}
.nav-burger.is-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.nav-burger:hover{background:var(--hi)}
/* ---- the mobile menu: a full-screen modal, not an in-flow sheet -----------
   It used to be a block that pushed the page down, which meant the header had
   to be un-stuck and the window scrolled to top just to make the menu reachable
   (see the old `.nav:has(.mobile-nav.is-open){position:static}` hack, now gone).
   A fixed layer needs none of that and gives the drill-down somewhere to live.

   100dvh, not 100vh: on mobile Safari 100vh is the LARGE viewport, so the last
   row of the menu sits behind the URL bar until you scroll. The vh line is the
   fallback for engines without dvh. */
.mobile-nav{display:none;position:fixed;inset:0;height:100vh;height:100dvh;z-index:var(--z-overlay);flex-direction:column;background:var(--card);padding:0;border:0}
/* Every tappable row is a 48px-minimum flex row: min-height guarantees the
   touch target whatever the font tokens resolve to, flex centres the label. */
.mobile-nav a{display:flex;align-items:center;min-height:48px;padding:10px 4px;font-family:var(--sans);font-weight:600;font-size:var(--fs-lg);letter-spacing:0;text-transform:none;color:var(--ink);border-bottom:var(--frame) solid var(--hair)}
.mobile-nav a:last-child{border-bottom:0}
@media (hover:hover){.mobile-nav a:hover{background:var(--hi)}} /* hover-capable only: taps must not leave a stuck highlight */
/* Keyboard focus must be visible on every row, same ring as the bar buttons.
   Inset offset: the rows span the full column, an outer ring would clip. */
.mobile-nav a:focus-visible,.mnav-group__btn:focus-visible{outline:3px solid var(--hi-2);outline-offset:-3px}
/* The page behind a full-screen modal must not scroll. overflow:hidden alone
   is ignored by iOS Safari touch gestures — the page still scrolls under the
   menu AND the gesture never reaches the menu's own scrolling panels, so a
   panel taller than the screen becomes unreachable. Fixing <body> in place is
   the pattern that actually holds on iOS; the JS stores the scroll offset in
   body.style.top on open and restores it on close (initMobileNav/setOpen). */
html.sm-navlock{overflow:hidden}
html.sm-navlock body{position:fixed;left:0;right:0;width:100%;overflow:hidden}

/* ---- modal bar: Back · title · Close ---- */
.mnav__bar{flex:none;display:flex;align-items:center;gap:10px;min-height:56px;padding:0 12px 0 16px;border-bottom:var(--frame) solid var(--hair);background:var(--card)}
.mnav__title{font-family:var(--sans);font-weight:800;font-size:var(--fs-2xl);letter-spacing:0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mnav__back{display:inline-flex;align-items:center;gap:6px;flex:none;background:transparent;border:0;padding:10px 8px 10px 0;margin:0;font-family:var(--sans);font-weight:700;font-size:var(--fs-md);color:var(--hi-2);cursor:pointer}
.mnav__back span{font-size:var(--fs-3xl);line-height:.7;font-weight:400}
.mnav__back[hidden]{display:none}
/* 44px minimum: this is the only way out of a full-screen modal. */
.mnav__close{margin-left:auto;flex:none;display:grid;place-items:center;width:44px;height:44px;background:transparent;border:0;padding:0;color:var(--ink);cursor:pointer}
.mnav__close svg{width:22px;height:22px;display:block}
.mnav__close:focus-visible,.mnav__back:focus-visible{outline:3px solid var(--hi-2);outline-offset:-3px}

/* ---- the level stack ----
   The root sits at the bottom and never moves; a category panel or the search
   view slides in over it and Back slides it away. Each level scrolls on its
   own, so going deeper does not inherit the root's scroll position. */
.mnav__stack{position:relative;flex:1 1 auto;min-height:0;overflow:hidden}
/* Bottom padding carries the safe-area inset so the last row clears the iPhone
   home indicator / Safari bottom bar and Android gesture nav; env() is 0 where
   there is none. overscroll-behavior keeps a fast fling from chaining into the
   page behind the modal. */
/* NO -webkit-overflow-scrolling:touch here: on iOS it puts the scroller on a
   legacy momentum layer that frequently FREEZES for absolutely-positioned
   containers that appear via visibility+transform — the panel slides in but
   cannot be scrolled at all. iOS ≥13 scrolls smoothly without it. */
/* overflow-x:hidden explicitly — overflow-y:auto silently promotes the x-axis
   from visible to auto, and any 1px protrusion turns into a sideways-scrollable
   menu. */
.mnav__level,.mobile-nav .mnav-group__panel{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;background:var(--card);padding:2px 20px calc(24px + env(safe-area-inset-bottom, 0px))}
/* z-index, not just source order: a level is `position:absolute` and so is
   anything positioned inside the root (the CTA's `position:relative` live dot,
   for one). With both at z-index:auto the later element in the DOM wins, and
   the root's red CTA dot painted straight through an open category panel. */
.mnav__level--root{transform:none;visibility:visible;z-index:1}
.mnav__level.is-active,.mobile-nav .mnav-group__panel.is-active{z-index:2}
/* visibility flips INSTANTLY on the way in and only after the slide on the way
   out. Transitioning it symmetrically means the level is still
   visibility:hidden for 220ms after it opens — and .focus() on a hidden
   element is a no-op, so the search field never took the keyboard. */
.mnav__level:not(.mnav__level--root),.mobile-nav .mnav-group__panel{transform:translateX(100%);visibility:hidden;transition:transform .22s ease,visibility 0s linear .22s}
.mnav__level.is-active,.mobile-nav .mnav-group__panel.is-active{transform:none;visibility:visible;transition:transform .22s ease,visibility 0s}
@media (prefers-reduced-motion: reduce){
  .mnav__level,.mobile-nav .mnav-group__panel{transition:none}
}
/* Inside the modal a panel is a whole screen, not a disclosure under a button:
   it needs no top rule and no border of its own. */
.mobile-nav .mnav-group__panel{border-top:0}
/* Drill-down chevron: the caret no longer flips, it points the way in. */
.mnav-group__chev{transform:none!important;font-size:var(--fs-3xl);line-height:.7;font-weight:400;color:var(--mute)}

/* ---- the search field at the top of the root level ----
   The real form.nav-search initSuggest() binds to — not a proxy button that
   opened a separate, different-looking screen (that level is gone,
   2026-08-20). Suggestions drop down over the menu list below the field. */
.mobile-nav .nav-search--full{display:flex;align-items:center;gap:11px;height:auto;margin:10px 0 8px;padding:0 0 0 14px;border:var(--frame) solid var(--ink);background:var(--card)}
.mobile-nav .nav-search__ic{width:19px;height:19px;flex:none;color:var(--mute)}
/* 16px input, not 15: anything below 16px makes iOS Safari ZOOM the page on
   focus, which reads as a horizontal-scroll glitch when the suggestions open. */
.mobile-nav .nav-search--full .nav-search__input{flex:1;width:auto;min-width:0;height:50px;font-size:var(--fs-lg);padding:0}
/* No separator bar before the loupe (design 2026-08-20). */
.mobile-nav .nav-search--full .nav-search__btn{flex:none;width:50px;height:50px;border-left:0}
.mobile-nav .nav-sugg{top:calc(100% + 8px);left:0;right:0;width:auto;max-height:min(480px,60dvh)}
/* ---- mobile search modal (≤980px) --------------------------------------
   Slides in from the RIGHT over whatever was there (a page, or the open burger
   menu), so it reads as going one level deeper rather than as a new context.
   Same visibility trick as the nav levels: flip it instantly on the way in so
   .focus() lands on a visible element (iOS ignores focus on visibility:hidden
   and the keyboard never comes up), and only after the slide on the way out.
   z-index --z-searchmodal (1010), NOT one above --z-overlay: the burger menu's
   --z-overlay ranks it inside <header class="nav">, and that header is lifted to
   --z-toast (1000) while the menu is open, so the modal has to clear 1000 — see
   the token's comment. 100dvh, not 100vh: 100vh is the LARGE viewport on mobile
   Safari, which would push the last result row behind the URL bar. */
.smodal{position:fixed;inset:0;height:100vh;height:100dvh;z-index:var(--z-searchmodal);display:flex;flex-direction:column;background:var(--card);transform:translateX(100%);visibility:hidden;transition:transform .26s ease,visibility 0s linear .26s}
.smodal.is-open{transform:none;visibility:visible;transition:transform .26s ease,visibility 0s}
/* Desktop keeps the inline dropdown — only the full-screen surface is phone-only.
   display:none also parks the modal for good above 980px, so a stale is-open
   from a rotation cannot paint over the desktop layout. */
@media (min-width:981px){.smodal{display:none}}
@media (prefers-reduced-motion: reduce){.smodal{transition:none}}
/* The FORM is the whole modal, not just the bar — initSuggest appends its
   .nav-sugg panel to the form, and this is what lets the results own everything
   under the bar and scroll by themselves. */
.smodal .nav-search--modal{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;height:auto;width:auto;border:0;background:transparent;padding:0;gap:0}
.smodal__bar{flex:none;display:flex;align-items:center;gap:8px;min-height:64px;padding:0 12px 0 4px;border-bottom:var(--frame) solid var(--hair);background:var(--card)}
/* 44px minimum: this is the way out of a full-screen modal. */
.smodal__back{flex:none;display:grid;place-items:center;width:44px;height:44px;background:transparent;border:0;padding:0;margin:0;color:var(--ink);cursor:pointer}
.smodal__back svg{width:22px;height:22px;display:block}
.smodal__back:focus-visible,.smodal .nav-search__btn:focus-visible{outline:3px solid var(--hi-2);outline-offset:-3px}
/* --fs-lg is 16px: anything smaller and iOS Safari zooms the page on focus,
   which reads as the modal jumping sideways. */
/* The lined field. --nav-line, not --hair: the live token overrides set --hair
   to #ffffff, so a --hair rule is an invisible rule. --nav-line is a literal
   rgba kept for exactly this (see the token block). :focus-within moves the line
   to --ink so the field also reads as ACTIVE once you are typing in it. */
.smodal__field{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:9px;height:46px;padding:0 12px;border:1px solid var(--nav-line);border-radius:var(--radius-btn);background:var(--card)}
.smodal__field:focus-within{border-color:var(--ink)}
.smodal__field .nav-search__ic{flex:none;width:18px;height:18px;color:var(--mute)}
/* text-overflow on an input DOES ellipsise its placeholder in Chrome/Safari.
   At 390px a long hint ("Marke suchen, z. B. Emma oder TEMPUR …") cannot fit
   next to a back button and a submit button, and a hard mid-word cut
   ("z. B. Emr") reads as broken where an ellipsis reads as deliberate. */
.smodal .nav-search__input{flex:1 1 auto;min-width:0;width:auto;height:100%;font-size:var(--fs-lg);padding:0;border:0;background:transparent;text-overflow:ellipsis}
/* The dialog takes focus on open; it is not a keyboard target, so no ring. */
.smodal:focus{outline:none}
/* Compact: every pixel here comes off the input's usable width. */
.smodal .nav-search__btn{flex:none;width:auto;height:40px;padding:0 13px;border:0;border-left:0;background:var(--ink);color:#fff;font-family:var(--sans);font-size:var(--fs-sm);font-weight:700;border-radius:var(--radius-btn)}
.smodal .nav-search__btn:hover{background:var(--hi);color:var(--ink)}
/* In the modal the suggestion panel is not a dropdown: it IS the screen. Static
   flow, no frame, no shadow, its own scroller, and the safe-area inset so the
   last row clears the home indicator. */
.smodal .nav-sugg{position:static;flex:1 1 auto;min-height:0;top:auto;left:auto;right:auto;width:auto;max-height:none;border:0;box-shadow:none;background:var(--card);overflow-y:auto;overscroll-behavior:contain;padding-bottom:calc(24px + env(safe-area-inset-bottom, 0px))}
/* Roomier rows than the desktop dropdown — these are the primary targets here. */
.smodal .nav-sugg .nav-sugg__item{min-height:60px;padding:10px 16px}
.smodal .nav-sugg__lbl{padding:16px 16px 6px}
.smodal .nav-sugg__empty{padding:18px 16px}
/* A phone tap must not leave a stuck highlight. */
@media (hover:none){.smodal .nav-sugg .nav-sugg__item:hover{background:transparent}}

/* ---- mobile nav accordion (mega content surfaced ≤980px) ---- */
.mnav-group{border-bottom:var(--frame) solid var(--hair)}
.mnav-group:last-child{border-bottom:0}
/* Root rows sit at 52px, panel rows at 48px: the two-step keeps the top level
   reading as the bigger unit without any extra whitespace. */
.mnav-group__btn{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:52px;padding:12px 4px;background:transparent;border:0;font-family:var(--sans);font-weight:600;font-size:var(--fs-lg);letter-spacing:0;text-transform:none;color:var(--ink);text-align:left;cursor:pointer}
@media (hover:hover){.mnav-group__btn:hover{background:var(--hi)}}
.mnav-group__chev{font-size:var(--fs-xs);line-height:1;transition:transform .18s;flex:none}
.mnav-group.is-open>.mnav-group__btn .mnav-group__chev{transform:rotate(180deg)}
.mnav-group__panel{background:transparent;border-top:var(--frame) solid var(--hair)} /* design: panels take the sheet bg, no own fill */
/* Parent comparison — first and emphasised inside an opened category group,
   mirroring the desktop panel's header row. */
.mnav-parent{display:flex;align-items:center;gap:10px;padding:11px 4px;border-bottom:var(--frame-hair) solid var(--hair);color:var(--ink)}
.mnav-parent b{font-family:var(--sans);font-weight:800;font-size:var(--fs-md);min-width:0}
.mnav-parent small{flex:none;font-family:var(--sans);font-weight:800;font-size:10.5px;line-height:1.5;letter-spacing:.1em;text-transform:uppercase;color:var(--ink);background:var(--hi);padding:3px 8px;border-radius:var(--radius-btn);white-space:nowrap} /* same chip as .mega-h__n */
.mnav-parent i{margin-left:auto;font-style:normal;font-weight:800;color:var(--hi-2);flex:none}
.mnav-sub{list-style:none;margin:0;padding:0}
.mnav-sub li{margin:0}
/* Panel rows: flush with the kicker labels (the 20px indent was the accordion's,
   a full-screen level does not need it), 48px touch height from the generic
   `.mobile-nav a` rule, one size up from fs-sm — these are the primary rows of
   a whole screen now, not fine print under a button. */
.mnav-sub a{padding:4px;font-family:var(--sans);font-weight:600;font-size:var(--fs-base);letter-spacing:.02em;text-transform:none;color:var(--ink)}
.mnav-sub li:last-child a{border-bottom:0}
@media (hover:hover){.mnav-sub a:hover{background:var(--hi)}}
/* "Alle …" rows mirror the desktop mega-link--all: bold ink + accent arrow,
   no separator line */
/* var(--blue) = the design's Sleep Blue kicker token (darker than the site accent) */
.mnav-sub__label{padding:16px 4px 2px;font-family:var(--sans);font-weight:800;font-size:var(--fs-2xs);letter-spacing:.14em;text-transform:uppercase;color:var(--blue)}
/* two classes: `.mnav-sub a` (class+type) outranks a lone class, so the bold
   styling silently lost — keep the row visibly BOLD (sentence case, no rule: design) */
.mnav-sub .mnav-sub__all{font-weight:800;font-size:var(--fs-md);color:var(--ink);margin-top:6px;padding-bottom:13px}
.mnav-sub__all::after{content:"\2192";margin-left:8px;color:var(--hi-2);font-weight:800}
/* Category panels only (design 2026-08-20; Rabatte/Ratgeber keep their
   labelled layout): no kicker labels, tighter 44px rows so the panel fits a
   phone screen, and a leading arrow on every row — the pillar (.mnav-parent
   above the list) is the one row without one. */
.mnav-sub--cat a{min-height:44px;padding:2px 4px 2px 0;font-size:var(--fs-base)}
.mnav-sub--cat a::before{content:"\2192";margin-right:10px;color:var(--hi-2);font-weight:800;flex:none}
/* Star article — the desktop mega's "Redaktions-Tipp" card, compacted for
   the Ratgeber panel. */
.mobile-nav .mnav-star{display:flex;align-items:center;gap:12px;margin:12px 0 14px;padding:9px 10px;border:var(--frame) solid var(--line);background:var(--card);color:var(--ink);letter-spacing:.02em;text-transform:none}
/* The in-panel row rule (.mnav-group__panel a{border-bottom:0}) outranks the
   two-class selector above and beheaded the card's bottom edge — the type
   selector restores the full frame, in both skins. */
.mobile-nav .mnav-group__panel a.mnav-star{border-bottom:var(--frame) solid var(--line)}
.nav--light .mobile-nav .mnav-group__panel a.mnav-star{border:1px solid var(--nav-line)}
@media (hover:hover){.mobile-nav .mnav-star:hover{background:var(--hi)}}
.mnav-star__img{flex:none;width:86px;height:60px;overflow:hidden;background:var(--paper)}
.mnav-star__img img{width:100%;height:100%;object-fit:cover;display:block}
.mnav-star__t{display:flex;flex-direction:column;gap:2px;line-height:1.3;min-width:0}
.mnav-star__t small{font-size:var(--fs-2xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--hi-2)}
.mnav-star__t b{font-size:var(--fs-md);font-weight:700}
/* Score chip on the featured-product card (category panels) — the desktop
   mega-feat's x.x/10, compacted. */
.mnav-star__score{margin-left:auto;flex:none;font-family:var(--sans);font-weight:800;font-size:var(--fs-md);color:var(--ink);background:var(--hi);padding:4px 8px}
.mnav-star__score i{font-style:normal;font-weight:700;font-size:var(--fs-2xs);opacity:.7}
/* The Rabatte panel's star is a BRAND, so its image is a logo, not a photo:
   contained on the card's own paper rather than cropped by the object-fit:cover
   the product/article stars want. Third line carries the live deal. */
.mobile-nav .mnav-star--brand .mnav-star__img{background:var(--card);display:grid;place-items:center;padding:6px}
.mobile-nav .mnav-star--brand .mnav-star__img img{width:100%;height:100%;object-fit:contain}
.mnav-star__t i{font-style:normal;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.02em;color:var(--hi-2)}
/* ---- mobile panel CTAs (design: mobile panel) --------------------------
   two-class selectors on the anchors: must outrank generic `.mobile-nav a` */
/* No own bottom margin: the level's safe-area padding is the gap below it.
   a.mnav-cta, not .mnav-cta: the CTA is the root level's last child, and the
   generic `.mobile-nav a:last-child{border-bottom:0}` (0-2-1) outranks a
   two-class selector — the extra type keeps the CTA's full frame intact. */
.mobile-nav a.mnav-cta{display:flex;align-items:center;justify-content:center;gap:10px;min-height:52px;margin:14px 0 0;padding:12px 16px;background:var(--hi);border:var(--frame) solid var(--ink);border-bottom:var(--frame) solid var(--ink);box-shadow:var(--shadow-2);font-weight:700;font-size:var(--fs-base);letter-spacing:.02em;text-transform:none;color:var(--ink)}
@media (hover:hover){.mobile-nav .mnav-cta:hover{background:var(--ink);color:#fff}}
/* Weitere block: compact ROWS, not boxed cards (user sketch 2026-08-20) —
   the cards cost ~90px a category panel cannot spare on a phone. Icon +
   label + trailing arrow, same 44px rhythm as the comparison list above;
   the one-line subtitles are hidden. */
.mnav-cards{display:flex;flex-direction:column;gap:0;padding:0 0 8px}
.mobile-nav .mnav-card,.mobile-nav .mnav-cards .mnav-card{display:flex;align-items:center;gap:11px;border:0;background:transparent;padding:2px 4px;min-height:44px;color:var(--ink);letter-spacing:.02em;text-transform:none}
@media (hover:hover){.mobile-nav .mnav-card:hover{background:var(--hi)}}
.mnav-card__ic{flex:none;width:18px;height:18px;color:var(--hi-2)}
.mnav-card__ic svg{width:100%;height:100%;display:block}
/* The live-deal row leads with the 9px pulse dot, the others with 18px icons —
   center the dot in the same 18px slot so all three marks share one axis and
   the labels start on one line. */
.mobile-nav .mnav-card .mega-band__dot{margin:0 4.5px}
.mnav-card__t{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.mnav-card__t b{font-size:var(--fs-base);font-weight:600}
.mnav-card__t small{display:none}
.mobile-nav .mnav-card i{font-style:normal;margin-left:auto;color:var(--hi-2);font-weight:800}
.mobile-nav .mnav-card--live{background:transparent}
/* Rabatte group: "Nach Kategorie" rows — red live dot + count (design) */
.mnav-sub .mnav-deal{display:flex;align-items:center;gap:10px}
.mnav-deal__dot{width:9px;height:9px;border-radius:50%;background:var(--neg);flex:none}
.mnav-deal b{font-weight:700;min-width:0}

/* ---- lowkey inline TOC ---- */
.toc-inline{display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;padding:20px 0;border-top:2px solid var(--line);border-bottom:2px solid var(--line);margin:30px 0}
.toc-inline span{font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--mute)}
.toc-inline a{font-weight:600;color:var(--soft);text-decoration:underline;text-decoration-color:var(--hair);text-underline-offset:4px;text-decoration-thickness:2px}
.toc-inline a:hover{color:var(--ink);text-decoration-color:var(--hi)}

/* ---- big intro paragraph ---- */
.lead-xl{font-family:var(--serif);font-weight:500;font-size:var(--fs-h3);line-height:1.42;max-width:62ch;color:var(--ink);margin:34px 0 0}
.lead-xl .hl{font-weight:700}
.best-for{display:inline-block;font-family:var(--sans);font-size:var(--fs-xs);font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--hi-2);margin-bottom:6px}
/* "best for" as a tab sitting on the product image */
.itemfig{position:relative;margin-bottom:18px}
.itemfig image-slot{display:block;width:100%;height:212px;border:var(--frame) solid var(--ink)}
.itemfig__tab{position:absolute;left:0;top:16px;background:var(--hi);color:var(--ink);border:var(--frame) solid var(--ink);border-left:0;font-family:var(--sans);font-weight:800;font-size:var(--fs-xs);letter-spacing:.05em;text-transform:uppercase;padding:7px 13px;box-shadow:var(--shadow-2);z-index:2}

/* ---- blog article ---- */
.article{max-width:760px;margin:0 auto}
.article__meta{display:flex;align-items:center;gap:14px;flex-wrap:wrap;font-size:var(--fs-sm);color:var(--mute);font-weight:600;letter-spacing:.03em;margin-bottom:18px}
.article__cat{background:var(--hi);color:var(--ink);padding:5px 11px;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.article h1{font-size:clamp(34px,5vw,58px);line-height:1.02;margin-bottom:18px}
.prose{font-size:19px;line-height:1.75}
.prose p{margin:0 0 26px;color:var(--prose-ink)}
.prose h2{font-size:clamp(28px,3.4vw,38px);margin:48px 0 16px}
.prose h3{font-size:25px;margin:34px 0 12px}
.prose ul,.prose ol{margin:0 0 26px;padding-left:24px}
.prose li{margin-bottom:9px}
.prose a{box-shadow:inset 0 -3px var(--hi);font-weight:600}
.prose blockquote{margin:30px 0;border-left:5px solid var(--hi);padding:6px 0 6px 24px;font-family:var(--serif);font-style:italic;font-size:var(--fs-4xl);line-height:1.4}
.prose img,.prose image-slot{display:block;width:100%;margin:30px 0;border:var(--frame) solid var(--line)}
/* …but a cert chip's logo is UI, not editorial imagery. The rule above is for
   full-width photos, and it framed each 22px seal logo and pushed it 30px clear
   of its own label inside the seal table. Same-specificity selectors could not
   win from .certchip's block, so reset here where the context is obvious. */
.prose .certchip img{width:auto;margin:0;border:0}
.prose .certchip{box-shadow:none} /* .prose a's yellow underline, not wanted on a chip */
/* A chip row under a section heading belongs to the heading, so it sits tight
   under it and keeps its distance from the paragraph that follows. */
.prose .certchips{margin:-4px 0 20px}
.prose .tbl .certchips{margin:0} /* inside a table cell the padding already spaces it */
.prose::after{content:"";display:table;clear:both}

/* ---- article media: [sm_img] / [sm_video] ----
   The figure carries the size (a PERCENTAGE of the 760px prose column, never
   px); the media inside always fills its figure. width:100% is the default,
   so nothing has to be picked in the editor. */
.sm-fig{margin:34px 0;max-width:100%}
.sm-fig--center{margin-left:auto;margin-right:auto}
.sm-fig--left{float:left;margin:8px 32px 22px 0}
.sm-fig--right{float:right;margin:8px 0 22px 32px}
.prose .sm-fig img,.prose .sm-fig video{display:block;width:100%;height:auto;margin:0;border:var(--frame) solid var(--line);background:var(--hair)}
.sm-fig .sm-fig__link{box-shadow:none;display:block}
.sm-fig figcaption{font-size:var(--fs-sm);color:var(--mute);font-weight:600;margin-top:10px;padding-left:2px}
/* a chrome-free video must not flash the native overlay play button
   (iOS Safari paints one even without the controls attribute) */
.sm-vid--bare::-webkit-media-controls,
.sm-vid--bare::-webkit-media-controls-start-playback-button{display:none!important;-webkit-appearance:none}
.sm-vid--bare{pointer-events:none}

/* Core's own "Dateien hinzufügen" path (our fallback): its wrapper carries an
   inline px width that pinned captioned images to the chosen thumbnail size. */
.prose .wp-caption{width:auto!important;max-width:100%;margin:34px 0}
.prose .wp-caption-text,.prose .wp-caption dd{font-size:var(--fs-sm);color:var(--mute);font-weight:600;margin-top:10px;padding-left:2px;text-align:left}
.prose .alignleft{float:left;margin:8px 32px 22px 0;max-width:50%}
.prose .alignright{float:right;margin:8px 0 22px 32px;max-width:50%}
.prose .aligncenter{margin-left:auto;margin-right:auto}
.prose img.alignleft,.prose img.alignright{width:auto}

/* Phones: no floats, no fractional widths — everything goes full width. */
@media (max-width: 760px){
  .sm-fig,.prose .alignleft,.prose .alignright{width:100%!important;max-width:100%;float:none;margin:26px 0}
  .prose img.alignleft,.prose img.alignright{width:100%}
}
.cta-product{border:var(--frame-bold) solid var(--line);background:var(--card);display:grid;grid-template-columns:200px 1fr auto;gap:0;align-items:stretch;margin:40px 0;box-shadow:var(--shadow-6);overflow:hidden}
/* image carries NO border of its own (only the outer card frame) and may
   never bleed out of its cell */
.cta-product__media{position:relative;min-height:180px;overflow:hidden}
/* article links get the prose underline — CTA card links must not */
.prose .cta-product a{box-shadow:none}
.cta-product__media image-slot{position:absolute;inset:0;display:block;width:100%;height:100%;margin:0;border:0}
.cta-product__body{padding:24px 26px;display:flex;flex-direction:column;justify-content:center}
.cta-product__body .kicker{color:var(--hi-2);font-size:var(--fs-2xs)}
.cta-product__body h4{font-size:26px;margin:6px 0 6px}
.cta-product__body p{font-size:var(--fs-base);color:var(--soft);margin:0}
.cta-product__side{padding:24px 26px;border-left:var(--frame-bold) solid var(--line);display:flex;flex-direction:column;justify-content:center;align-items:center;gap:12px;text-align:center;min-width:210px}
.cta-product__side .bigscore{font-size:54px}
/* mobile stack: no rules around the image (the card frame is enough) and
   breathing room above the "Unsere Empfehlung" kicker */
@media (max-width: 760px){.cta-product{grid-template-columns:1fr}.cta-product__media{height:210px;min-height:0}.cta-product__body{padding:18px 20px 14px}.cta-product__side{border-left:0;border-top:var(--frame-bold) solid var(--line);padding:18px 20px 22px}}

/* ---- article TOC + author box ---- */
.article-toc{border:var(--frame) solid var(--line);background:var(--card);padding:20px 24px;margin:34px 0}
.article-toc h4{font-family:var(--sans);font-size:var(--fs-2xs);font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--mute);margin:0 0 12px}
.article-toc ol{list-style:none;margin:0;padding:0}
.article-toc li{border-top:var(--frame-hair) solid var(--hair)}
.article-toc li:first-child{border-top:0}
.article-toc a{display:flex;gap:12px;padding:9px 0;font-weight:600;font-size:var(--fs-lg);color:var(--soft)}
/* quiet arrow marker (numbers read too loud here) */
.article-toc a::before{content:"\2192";font-family:var(--sans);font-weight:700;color:var(--mute)}
.article-toc a:hover{color:var(--ink)}
.authorbox{border:var(--frame) solid var(--line);background:var(--card);padding:26px;display:flex;gap:20px;align-items:flex-start;margin:44px 0 0}
.authorbox__av{width:66px;height:66px;border-radius:50%;background:var(--hi);border:var(--frame) solid var(--ink);display:grid;place-items:center;font-family:var(--serif);font-weight:900;font-size:var(--fs-4xl);flex:none}
.authorbox__name{font-family:var(--serif);font-weight:800;font-size:var(--fs-3xl)}
.authorbox__role{font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--hi-2);margin:2px 0 10px}
.authorbox__bio{font-size:var(--fs-base);color:var(--soft);line-height:1.6}

/* ---- review: score summary + breakdown bars ---- */
/* Unsere Bewertung: the --card fill carries the panel, no frame around it. The
   overall score keeps ONE light hairline separating it from the four sub-scores
   (that split is the point of the panel) and the bar tracks lose their 2px ink
   outline — four outlined 9px pills were the densest lining on the review. */
.review-score{display:grid;grid-template-columns:auto 1fr;gap:34px;align-items:center;border:var(--frame-light) solid var(--line);background:var(--card);padding:30px}
.review-score__overall{text-align:center;padding-right:34px;border-right:1px solid var(--hair-soft)}
.review-score__overall .big{font-family:var(--serif);font-weight:900;font-size:74px;line-height:.78}
.review-score__overall .big sup{font-size:22px;color:var(--mute);font-weight:600}
.review-score__overall .verdict{margin-top:12px}
.scorebars{display:grid;grid-template-columns:1fr 1fr;gap:18px 34px}
.scorebar .lbl{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:7px}
.scorebar .lbl span{font-size:var(--fs-md);font-weight:700}
.scorebar .lbl b{font-family:var(--serif);font-weight:900;font-size:var(--fs-3xl)}
/* --hair-soft, not --hair: with the ink outline gone the UNFILLED part of the
   bar is the only thing showing the scale, and --hair is white in production —
   the bars read as plain blocks with nothing to measure against. */
.scorebar .track{height:9px;background:var(--hair-soft);border:var(--frame-light) solid var(--ink)}
.scorebar .fill{height:100%;background:var(--ink)}
@media (max-width: 760px){.review-score{grid-template-columns:1fr}.review-score__overall{border-right:0;border-bottom:1px solid var(--hair-soft);padding-right:0;padding-bottom:22px}.scorebars{grid-template-columns:1fr}}

/* ---- review: our-test firmness grid ---- */
.testgrid{display:grid;grid-template-columns:1fr 1fr;gap:28px}
.testcard{border:var(--frame) solid var(--line);background:var(--card);padding:24px}
.testcard h4{font-family:var(--sans);font-size:var(--fs-2xs);font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--mute);margin:0 0 18px}
/* ---- review: our-test firmness scale ---- */
.firm{margin-top:10px}
.firm__head{display:flex;align-items:baseline;gap:10px;margin-bottom:30px}
.firm__head b{font-family:var(--serif);font-weight:900;font-size:23px;line-height:1}
.firm__head span{font-weight:700;font-size:var(--fs-sm);color:var(--mute)}
.firm__track{position:relative;height:10px;background:var(--hair);border:var(--frame) solid var(--ink)}
.firm__fill{position:absolute;left:0;top:0;bottom:0;background:var(--ink)}
.firm__marker{position:absolute;top:50%;transform:translate(-50%,-50%);width:40px;height:40px;border-radius:50%;background:var(--hi);border:var(--frame) solid var(--ink);display:grid;place-items:center;font-family:var(--serif);font-weight:900;font-size:var(--fs-lg);color:var(--ink);box-shadow:var(--shadow-1)}
.firm__ends{display:flex;justify-content:space-between;margin-top:14px}
.firm__ends span{font-size:var(--fs-xs);font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink)}
.firm__ends span:nth-child(2){color:var(--mute)}
/* legacy firmness kept for safety */
.firm-scale{display:flex;gap:5px;margin-top:14px}
.firm-scale .s{flex:1;height:40px;border:var(--frame) solid var(--ink);background:var(--card);display:grid;place-items:center;font-family:var(--serif);font-weight:800;font-size:var(--fs-base);color:var(--mute)}
.firm-scale .s.fill{background:var(--tint-cool)}
.firm-scale .s.on{background:var(--hi);color:var(--ink)}
.firm-bar__pin::after{content:attr(data-label);position:absolute;bottom:34px;left:50%;transform:translateX(-50%);font-family:var(--serif);font-weight:800;font-size:var(--fs-md);white-space:nowrap;background:var(--hi);padding:2px 8px;border:var(--frame) solid var(--ink)}
.firm-ends{display:flex;justify-content:space-between;margin-top:10px;font-size:var(--fs-xs);font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--mute)}
.suit{display:flex;flex-direction:column;gap:0}
.suit__row{display:grid;grid-template-columns:1fr auto;align-items:center;gap:14px;padding:11px 0;border-top:var(--frame-hair) solid var(--hair)}
.suit__row:first-child{border-top:0}
.suit__label{font-weight:600;font-size:var(--fs-base)}
.dots{display:flex;gap:5px}
.dot{width:14px;height:14px;border-radius:50%;border:var(--frame) solid var(--ink)}
.dot.on{background:var(--ink)}
@media (max-width: 760px){.testgrid{grid-template-columns:1fr}}

/* ---- review: gallery ---- */
.gallery{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:185px;gap:16px}
/* Tiles carry no frame — the photos butt straight against the paper. Covers the
   review gallery AND the comparison .gallery-feature, which reuses this rule. */
.gallery figure{margin:0;border:var(--frame-light) solid var(--line);background:var(--card);position:relative;overflow:hidden}
.gallery figure:first-child{grid-column:span 2;grid-row:span 2}
.gallery image-slot{display:block;width:100%;height:100%}
.gallery figure img{width:100%;height:100%;object-fit:cover;display:block;position:absolute;inset:0}
.gallery figure video{width:100%;height:100%;object-fit:cover;display:block;position:absolute;inset:0}
.gallery figcaption{position:absolute;left:0;bottom:0;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:#fff;background:var(--scrim);padding:7px 11px}
@media (max-width: 760px){.gallery{grid-template-columns:repeat(2,1fr);grid-auto-rows:150px}.gallery figure:first-child{grid-column:span 2;grid-row:span 1}}

/* ---- review: verdict box ---- */
.verdict-box{border:var(--frame-bold) solid var(--ink);background:var(--dark);color:#fff;padding:34px;box-shadow:var(--shadow-6);display:grid;grid-template-columns:1fr auto;gap:30px;align-items:center}
.verdict-box h3{color:#fff;font-size:var(--fs-5xl);margin-bottom:12px}
.verdict-box p,.verdict-box__text{color:var(--on-dark-soft);font-size:var(--fs-lg);max-width:54ch}
.verdict-box__main{display:flex;flex-direction:column;gap:20px}
.verdict-box__score{text-align:center;flex:none;padding-left:34px;border-left:var(--frame) solid rgba(255,255,255,.15)}
.verdict-box__score .bigscore{font-size:72px;line-height:.8;color:var(--hi);letter-spacing:-0.04em}
.verdict-box__score .bigscore sup{font-size:var(--fs-3xl);color:var(--on-dark-soft);font-weight:600}
.verdict-box__score .big{font-family:var(--serif);font-weight:900;font-size:72px;line-height:.8;color:var(--hi)}
.verdict-box__score .of{font-size:var(--fs-xs);color:var(--on-dark-soft);font-weight:700;letter-spacing:.08em;text-transform:uppercase}
@media (max-width: 620px){.verdict-box{grid-template-columns:1fr;box-shadow:var(--shadow-4)}.verdict-box__score{padding-left:0;border-left:0;border-top:var(--frame) solid rgba(255,255,255,.15);padding-top:24px}}

/* ---- review: in-text image with caption ---- */
.figfull{margin:34px 0}
.figfull image-slot{display:block;width:100%;height:340px;border:var(--frame) solid var(--line)}
.figfull figcaption{font-size:var(--fs-sm);color:var(--mute);font-weight:600;margin-top:10px;padding-left:2px}

/* ---- "Test & Review" badge ---- */
.review-flag__star{line-height:1}
.review-flag{display:inline-flex;align-items:center;gap:8px;background:var(--hi);color:var(--ink);font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:7px 13px;border:var(--frame) solid var(--ink)}

/* ---- hero meta items (product review page) ---- */
.hero__meta-item{font-size:var(--fs-base);font-weight:700;color:var(--ink);white-space:nowrap}
.hero--dark .hero__meta-item{color:var(--on-dark)}
.hero__meta-item strong{font-family:var(--serif);font-size:var(--fs-2xl);font-weight:900;color:#fff;margin-right:4px}

/* ---- Stiftung Warentest note ---- */
.sw-note{font-size:var(--fs-sm);color:var(--mute);margin-top:10px;font-weight:600;letter-spacing:.02em}
.sw-note strong{color:var(--ink);font-weight:700}
/* Warentest grade as its own full-width row beneath the split. It must NOT sit
   inside .review-score__overall: that grid column is `auto`, so the grade line
   widened it and pushed the vertical divider right. */
.review-score__note{grid-column:1/-1;margin-top:2px;padding-top:14px;border-top:1px solid var(--hair-soft);text-align:left}

/* ---- stamp size modifier ---- */
.stamp--lg{font-size:var(--fs-md);padding:8px 16px}

/* ---- buycard trust badges ---- */
.buycard__trust{display:flex;flex-direction:column;gap:5px;padding:10px 0 0}
.buycard__trust-item{font-size:var(--fs-md);color:var(--ink);font-weight:700;padding-left:18px;position:relative}
.buycard__trust-item::before{content:'✓';position:absolute;left:0;color:var(--hi);font-weight:900}
/* disclosure hugs the CTA it labels — no divider line, minimal gap */
.buycard__disc{font-size:var(--fs-2xs);color:var(--mute);text-align:center;margin:5px 0 0;letter-spacing:.03em}

/* ---- product hero: featured image right column ---- */
.hero__media{margin:0;align-self:center;min-width:0}
.hero__media img{display:block;width:100%;height:400px;object-fit:cover;border:var(--frame-bold) solid var(--paper);box-shadow:var(--shadow-6)}
.hero__grid--single{grid-template-columns:1fr}
.hero__lead-text{color:var(--on-dark-soft);font-size:var(--fs-2xl);line-height:1.55;max-width:54ch;margin-top:16px}
.no-data{color:var(--mute);font-size:var(--fs-base)}
@media (max-width: 880px){.hero__media img{height:300px;box-shadow:var(--shadow-4)}}

/* ---- pros/cons separation from score card ---- */
.review-score + .proscons{margin-top:28px}

/* ---- anchor offset under sticky nav + subnav ---- */
/* Measured, not guessed: the old flat 150px cleared the 134px desktop nav by
   16px but wasted 80px on the 70px mobile bar. */
.main-col > section{scroll-margin-top:calc(var(--nav-h) + var(--subnav-h) + 24px)}

/* ── Comparison archive: editorial index rows (.comp-row) ──────────── */
.comp-sec{padding:0;margin-bottom:64px}
.comp-sec:last-of-type{margin-bottom:0}
.comp-rows{border-top:var(--frame-bold) solid var(--ink)}
/* directly under a section head the container's own top rule doubles the
   head's bottom rule — one bar is enough */
.sec__head + .comp-rows{border-top:0}
/* Row separator on the 1px --nav-line hairline, like the FAQ rows, the tables
   and the article lattice. The thumbnail frame (.comp-row__media), the count
   chip and the container's own opening rule keep their weight — they frame or
   open, they do not divide. */
.comp-row{display:grid;grid-template-columns:230px minmax(0,1fr) auto;gap:30px;align-items:center;padding:30px 12px;margin:0 -12px;border-bottom:1px solid var(--nav-line);transition:background .15s}
.comp-row:hover{background:var(--card)}
.comp-row__media{display:block;border:var(--frame) solid var(--ink);background:var(--paper)}
.comp-row__media img{display:block;width:100%;height:150px;object-fit:cover}
.comp-row--lead .comp-row__media{border-width:3px;box-shadow:var(--shadow-6)}
.comp-row__ph{display:flex;align-items:center;justify-content:center;height:150px;font-family:var(--serif);font-weight:900;font-size:44px;color:var(--mute);background:var(--card)}
.comp-row__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;font-family:var(--sans);font-size:var(--fs-xs);font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--mute);margin:0 0 10px}
.comp-row__count{display:inline-block;background:var(--hi);color:var(--ink);border:var(--frame) solid var(--ink);padding:3px 9px;font-size:var(--fs-2xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;line-height:1.3;white-space:nowrap}
.comp-row__byline{color:var(--ink)}
.comp-row__title{font-family:var(--serif);font-size:var(--fs-h3);font-weight:800;line-height:1.12;letter-spacing:-.01em;margin:0 0 10px}
.comp-row__title a{color:inherit;text-decoration:none}
.comp-row__title a:hover{background:var(--hi);box-shadow:0 0 0 4px var(--hi)}
.comp-row__excerpt{font-size:var(--fs-lg);line-height:1.55;color:var(--soft);max-width:58ch;margin:0}
.comp-row__cta{display:flex;flex-direction:column;align-items:flex-end;gap:12px}
.comp-row__cta .stamp{margin-bottom:0}

@media (max-width: 880px){
  .comp-row{grid-template-columns:150px minmax(0,1fr);gap:16px 22px;align-items:start;padding:26px 0;margin:0}
  .comp-row:hover{background:transparent}
  .comp-row__media img,.comp-row__ph{height:110px}
  .comp-row__ph{font-size:32px}
  .comp-row--lead .comp-row__media{box-shadow:var(--shadow-3)}
  .comp-row__cta{grid-column:1/-1;flex-direction:row;align-items:center;justify-content:flex-start}
}

@media (max-width: 620px){
  .comp-row{grid-template-columns:1fr;gap:14px;padding:24px 0}
  .comp-row__media img,.comp-row__ph{height:180px}
  .comp-row__cta{flex-direction:column;align-items:stretch}
  .comp-row__cta .btn{width:100%}
  .comp-row__cta .stamp{align-self:flex-start}
  .comp-sec{margin-bottom:48px}
}

/* ── Reassurance bar (front-page.php) ────────────────────────────────────
   Originally the /rabatte/ hub's "sm_rabatte_stats = bar" variant; the hub
   dropped its stat header in the 2026-08-19 refonte and the FRONT PAGE is now
   the only caller (Design → Front page → stats bar). Kept verbatim, renamed
   only in this comment: front-page.php still emits the .rhub-statbar classes.

   ONE cell anatomy for all variants (the hero .stats language laid horizontal:
   serif number over kicker label, centered, no icons, no lining around items).
   Variants differ ONLY in surface. */
.rhub-statbar{background:var(--card);border-top:var(--frame) solid var(--line);border-bottom:var(--frame) solid var(--line)}
.rhub-statbar__row{display:flex;flex-wrap:wrap;padding:26px 0;gap:18px 12px}
.rhub-statbar__item{flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;text-align:center;gap:7px}
.rhub-statbar__item i{display:none}
.rhub-statbar__item b{font-family:var(--serif);font-weight:900;font-size:29px;line-height:1;white-space:nowrap}
.rhub-statbar__item span{font-size:var(--fs-2xs);font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--mute)}
/* B — ink band: a dark hero continues into the strip. */
.rhub-statbar--ink{background:var(--dark);border-top:0;border-bottom:0}
.rhub-statbar--ink .rhub-statbar__item b{color:#fff}
.rhub-statbar--ink .rhub-statbar__item span{color:var(--on-dark-soft)}
/* C — marker: a yellow highlighter swipe behind each number. */
.rhub-statbar--marker{background:var(--paper)}
.rhub-statbar--marker .rhub-statbar__item b{position:relative;z-index:0}
.rhub-statbar--marker .rhub-statbar__item b::after{content:"";position:absolute;left:-6px;right:-6px;bottom:-1px;height:40%;background:var(--hi);z-index:-1}
@media (max-width: 760px){
  /* 2×2 grid, breathing room top AND bottom (all variants share it).
     .wrap's mobile padding shorthand (0 16px) nukes vertical padding at
     lower specificity — THE original "no bottom padding" bug. */
  .rhub-statbar__row.wrap{gap:22px 10px;padding:22px 16px 26px}
  .rhub-statbar__item{flex:1 1 45%}
  .rhub-statbar__item b{font-size:var(--fs-4xl)}
  .rhub-statbar__item span{font-size:var(--fs-3xs);letter-spacing:.06em}
}

/* ================ Rabatt-Familie — hub, Kategorie, Marke ==================
   ONE design for the three discount page types (2026-08-19 refonte, handoff
   `design_handoff_rabatte`):

     /rabatte/                        archive-sm_discount.php
     /rabatte/kategorie/{slug}/       rabatte-kategorie.php
     /marken/{brand}/                 single-sm_brand.php

   They previously carried three unrelated skins (.rhub-*, .rcat-*, .deal),
   which this block replaces wholesale. The shared vocabulary is: the page head
   band (.sm-arch__head, borrowed from the archive refonte, so the Rabatt pages
   open exactly like /produkte/ does), the geprüft line, the offer row, the
   product card (the SITE-WIDE .pcard, with its discount slots turned on — not
   a copy), the brand tile and the method block. Nothing here is per-page: if a
   rule only one of the three needs, it hangs off that page's own wrapper.

   Token scope, same contract as .pcard and .sm-arch: production overrides
   --hair to #ffffff, --radius to 20px and --serif to IBM Plex Sans from
   Design > Design tokens, so this family pins those three on its own scope and
   inherits everything else (ink, soft, mute, hi, hi-2, pos, neg, dark, paper,
   card) from the live palette. That is also the answer to the prototypes'
   14px/10px/999px radii: the theme's card radius is 0, the archive and the
   product card are drawn square, and a rounded Rabatt page inside a square
   site would read as a different site. Square, hairline, Playfair display —
   drop these three lines if the global tokens are ever realigned. */
.sm-rab{
  --hair:#d5dde1;
  --radius:0;
  --serif:"Playfair Display", Georgia, serif;
  /* The fourth pinned value, for the same reason as the other three: this
     design distinguishes a "paper" fill from a "card" fill (the offer row's
     value cell, the category tiles) and production sets --paper to #ffffff,
     which collapses the two into one white block. Same literal the archive's
     score track uses (--sm-track), so the site has ONE subtle surface. */
  --sm-fill:#eef3f6;
  /* Sticky offsets consume the nav's MEASURED height (initNavHeight), never a
     re-measurement of our own. */
  --sm-rab-top:calc(var(--nav-h, 70px) + var(--subnav-h, 0px));
}
/* Anchor targets ("Wie wir Codes prüfen") clear the sticky header. */
.sm-rab [id]{scroll-margin-top:calc(var(--sm-rab-top) + 24px)}

/* ---- page head: the "Stand …" suffix and the geprüft row ----------------
   The band itself is .sm-arch__head — the SAME part every archive uses
   (template-parts/archive-head.php). Only these two additions are new. */
.sm-arch__title small{font-family:var(--sans);font-size:var(--fs-xl);font-weight:500;color:var(--mute);letter-spacing:0;white-space:nowrap}
.chkrow{display:flex;align-items:center;gap:18px;flex-wrap:wrap;row-gap:10px;margin-top:16px}
.chkrow__how{font-size:var(--fs-sm);font-weight:600;color:var(--ink);border-bottom:1px solid var(--hair);padding-bottom:2px}
.chkrow__how:hover{border-bottom-color:var(--ink)}
.chkrow .chkline{margin-left:auto}
.chkline{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;color:var(--soft)}
.chkline__dot{width:7px;height:7px;border-radius:50%;background:var(--pos);flex:none;font-style:normal;animation:sm-dotpulse 1.8s infinite}
/* NOT sm-livepulse: that name is already the nav/mega ring pulse defined
   further up, and a second @keyframes with the same name replaces it for the
   whole document. This dot throbs in place; that one expands and fades. */
@keyframes sm-dotpulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.5);opacity:.45}}
@media (prefers-reduced-motion: reduce){.chkline__dot{animation:none}}

/* ---- hub search: the site's own suggest widget, at hero size -------------
   form.nav-search + .nav-search__input is what initSuggest() binds to, so the
   hub search IS the nav search — same endpoint, same dropdown, same keyboard
   handling. Only the geometry is the hub's own. */
.rsearch{width:100%;max-width:560px;margin:0 auto;position:relative}
/* Scoped to .rsearch (0,2,0) ON PURPOSE: .nav-search's own 30px-strip rules sit
   LATER in this file, so an unscoped .nav-search--hub loses to them at equal
   specificity and the hero pill renders as the nav strip with a button hanging
   out of it. */
.rsearch .nav-search--hub{height:56px;align-items:stretch;gap:10px;padding:0 0 0 18px;border:var(--frame) solid var(--ink);background:var(--card)}
.rsearch .nav-search__ic{width:19px;height:19px;color:var(--mute);flex:none;align-self:center}
.rsearch .nav-search__input{flex:1;width:auto;font-size:var(--fs-lg);padding:0}
.rsearch .nav-search__btn{width:auto;padding:0 24px;border-left:0;background:var(--ink);color:#fff;font-family:var(--sans);font-size:var(--fs-md);font-weight:700}
.rsearch .nav-search__btn:hover{background:var(--hi);color:var(--ink)}
/* Overlay, never a push: absolute, its own scroll, full width of the bar. */
.rsearch .nav-sugg{left:-2px;right:-2px;width:auto;top:calc(100% + 8px);max-height:480px}
/* The right-hand value column initSuggest() renders from `it.right`. */
.nav-sugg__r{margin-left:auto;flex:none;display:flex;align-items:baseline;gap:7px;font-size:var(--fs-sm);font-weight:700;color:var(--hi-2);white-space:nowrap}
.nav-sugg__r .neg{color:var(--neg);font-weight:800}
.nav-sugg__empty{padding:14px;font-size:13.5px;line-height:1.5;color:var(--soft)}
.nav-sugg__empty a{font-weight:700;color:var(--hi-2)}

/* ---- hub Such-Kopf: centred head + the category chip row ---------------- */
.rhead{text-align:center}
.rhead > .wrap{max-width:780px;padding-top:34px;padding-bottom:40px;display:flex;flex-direction:column;align-items:center}
/* Trail on the page's normal left edge like every other head (2026-08-20):
   its own full-width wrap ABOVE the centered 780px content column, which
   only the title/lead keep. Overrides the `.rhead > .wrap` column styling. */
.rhead > .wrap.rhead__trail{max-width:1160px;display:block;padding:22px 32px 0}
.rhead .sm-arch__lead{max-width:58ch;margin-bottom:24px}
.catchips{display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:center;margin-top:18px}
.catchips .sm-chip small{color:var(--mute);font-weight:600}
.catchips .sm-chip i{font-style:normal;color:var(--hi-2);font-weight:800}

/* ---- Cover-Deal: the dark band under the hub head ----------------------- */
.cover{background:var(--dark);color:var(--on-dark)}
.cover > .cover__in{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:28px;align-items:center;padding:30px 32px;color:var(--on-dark)}
.cover__val{display:flex;flex-direction:column;gap:6px;flex:none}
.cover__k{display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-2xs);font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--hi)}
.cover__k em{width:8px;height:8px;border-radius:50%;background:var(--neg);flex:none;font-style:normal;animation:sm-dotpulse 1.6s infinite}
@media (prefers-reduced-motion: reduce){.cover__k em{animation:none}}
.cover__pct{font-family:var(--serif);font-weight:800;font-size:clamp(38px,5vw,54px);line-height:1}
.cover__body{min-width:0;display:flex;flex-direction:column;gap:7px}
.cover__title{font-family:var(--serif);font-weight:800;font-size:23px;line-height:1.2;margin:0;color:#fff}
.cover__meta{font-size:var(--fs-sm);color:var(--on-dark-mute)}
.cover__meta b{color:var(--pos)}
.cover__cta{flex:none;background:var(--hi);color:var(--ink);font-size:var(--fs-base);font-weight:700;padding:14px 24px;white-space:nowrap;transition:background .15s,color .15s}
.cover__in:hover .cover__cta{background:#fff}

/* ---- the offer row — THE shared skin ------------------------------------
   value | benefit | action, hairline-bordered, on card white. Every offer on
   every one of the three pages is this element (template-parts/offer-row.php). */
.offers{display:flex;flex-direction:column;gap:14px}
.offers__item{display:flex;flex-direction:column}
/* Progressive enhancement, the right way round: the overflow rows are VISIBLE
   in the markup and JS clamps them on init (initOffersMore). Hiding them in
   CSS first would leave a reader without JS looking at four of ninety-six
   offers and a button that does nothing. Same reason the button itself only
   appears once the clamp is actually in place. */
.offers__more{display:none;justify-content:center;margin-top:24px}
.offers.is-clamped .offers__item.is-more{display:none}
.offers.is-clamped + .offers__more{display:flex}
/* --hair-soft, not --hair: the live token overrides run --hair at #ffffff, so
   on a white --card this pill had no edge at all and read as loose text rather
   than as the button it is (the brand page's code archive is one of these).
   Same fix the other kept dividers use — a literal no override can erase. */
.pillbtn{background:var(--card);border:1px solid var(--hair-soft);border-radius:999px;font-family:var(--sans);font-size:var(--fs-md);font-weight:700;color:var(--ink);padding:12px 24px;cursor:pointer;transition:border-color .15s,background .15s}
.pillbtn:hover{border-color:var(--ink)}
.pillbtn:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}
/* The quiet size: a secondary door (the expired-code archive) sitting under a
   list of live offers, which must not out-shout them. */
.pillbtn--sm{font-size:var(--fs-sm);padding:8px 18px}

.offer{background:var(--card);border:1px solid var(--hair);display:grid;grid-template-columns:124px minmax(0,1fr) 220px;align-items:stretch}
.offer--expired{opacity:.55}
.offer__val{background:var(--sm-fill);color:var(--ink);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;padding:22px 12px;text-align:center}
.offer__val b{font-family:var(--serif);font-weight:800;font-size:30px;line-height:1}
.offer__val small{font-family:var(--sans);font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--mute)}
/* A written-out value ("Gratis Kissen") cannot hold 30px — it wraps into the
   badge and pushes the row. */
.offer__val--txt b{font-size:var(--fs-lg);line-height:1.2;letter-spacing:0}
.offer__main{padding:16px 22px;display:flex;flex-direction:column;gap:7px;justify-content:center;min-width:0}
.offer__kick{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.offer__brand{font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--hi-2)}
.offer__brand:hover{color:var(--ink)}
.offer__cat{font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--mute)}
.offer__flag{background:var(--ink);color:#fff;font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:3px 8px}
.offer__title{font-family:var(--sans);font-weight:700;font-size:19px;line-height:1.2;letter-spacing:-.015em;margin:0;overflow-wrap:break-word}
.offer__note{margin:0;font-size:var(--fs-md);line-height:1.5;color:var(--soft)}
.offer__meta,.offer__strip{font-size:var(--fs-xs);color:var(--mute)}
.offer__ok{color:var(--pos)}
.offer__sep{opacity:.55}
.offer__terms{border:0;margin:0;padding:0}
.offer__terms > summary{list-style:none;cursor:pointer;font-size:var(--fs-sm);font-weight:600;color:var(--soft);display:inline-flex;align-items:center;gap:6px;padding:0}
.offer__terms > summary::-webkit-details-marker{display:none}
.offer__terms > summary::after{content:"+";font-size:var(--fs-xs);color:var(--hi-2);font-weight:700}
.offer__terms[open] > summary::after{content:"–"}
.offer__terms > summary:hover{color:var(--hi-2)}
.offer__terms p{margin:6px 0 0;font-size:13.5px;line-height:1.55;color:var(--soft);max-width:62ch}
.offer__cta{padding:16px 20px;display:flex;flex-direction:column;gap:7px;justify-content:center;border-left:1px solid var(--hair)}
.offer__go{display:block;text-align:center;background:var(--ink);color:#fff;font-size:var(--fs-md);font-weight:700;padding:13px 12px;transition:background .15s,color .15s}
.offer__go:hover{background:var(--hi);color:var(--ink)}
.offer__sub{text-align:center;font-size:11.5px;color:var(--mute)}
.offer__sub--pos{font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--pos)}
.offer__cta .anz{margin-top:0}
/* full-width ✓-meta strip (brand page) */
.offer__strip{grid-column:1/-1;border-top:1px solid var(--hair);padding:9px 20px;display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap}
.offer__strip a{font-weight:700;color:var(--hi-2)}
.offer__strip a:hover{color:var(--ink)}
/* duplicate codes for the same offer */
.offers__dupes{border:1px solid var(--hair);border-top:0;background:var(--card)}
.offers__dupes > summary{list-style:none;cursor:pointer;padding:11px 22px;font-size:var(--fs-sm);font-weight:700;color:var(--soft)}
.offers__dupes > summary::-webkit-details-marker{display:none}
.offers__dupes > summary:hover{color:var(--hi-2)}
.offers__dupes-body{display:flex;flex-direction:column;gap:1px;background:var(--hair)}
.offers__dupes-body .offer{border:0}

/* ---- the code preview ("peek") ------------------------------------------
   Dashed box, masked code, ink cover with a sliver of the mask showing at the
   right edge. Same widget on all three pages (template-parts/dealcode.php,
   variant `peek`); the cover is a LINK to the brand page on the hub and the
   category pages and a reveal BUTTON on the brand page. The blur sits on the
   bullet placeholder, never on the real code — the code is only ever in
   data-code. */
.dealcode--peek{position:relative;display:block;border:0;background:none;padding:0;gap:0}
.dealcode--peek .dealcode__code{display:block;width:100%;box-sizing:border-box;text-align:right;padding:13px 12px 13px 0;border:1px dashed var(--hair);background:var(--card);font-size:var(--fs-md);letter-spacing:.17em}
/* right: the width of the code sliver left uncovered. The button is the thing
   you click, but the BLUR is the thing that makes you want to — leave it more
   than a hint. 66px shows roughly half the mask in a 180px row cell and most
   of it in the 274px sidebar rail. */
.dealcode--peek .dealcode__btn{position:absolute;top:0;left:0;right:66px;height:100%;display:flex;align-items:center;justify-content:center;font-size:var(--fs-sm);letter-spacing:0;text-transform:none;padding:0 8px;white-space:nowrap}
/* NO hover override for the peek variant (2026-08-21): `.dealcode__btn:hover`
   is the component's hover everywhere — invert to var(--hi) on var(--ink). The
   local override sent it to var(--hi-2) instead, so the ONE dealcode readers
   meet most often (the /rabatte/ hub's "Code ansehen", and the brand rail's)
   hovered a different colour from every other code button on the site.
   Geometry for the variant stays above; colour comes from the component. */
/* Revealed: the cover steps out of the way and becomes the copy button. The
   code CENTERS in its box — the right-alignment above exists only for the
   masked peek sliver. */
.dealcode--peek.revealed .dealcode__code{border-color:var(--ink);text-align:center;padding:13px 12px}
.dealcode--peek.revealed .dealcode__btn{position:static;width:100%;height:auto;padding:11px 14px;margin-top:7px}

/* ---- Produkt-Einschub / "Reduzierte {Kategorie}" ------------------------
   The accent band the hub drops into its feed, and the grid the category page
   opens with. The cards inside are the SITE-WIDE .pcard in discount mode. */
.dealsplit{background:var(--hi);padding:22px 24px 24px;margin:26px 0}
.dealsplit__head{display:flex;align-items:baseline;justify-content:space-between;gap:20px;margin-bottom:14px}
.dealsplit__head b{font-family:var(--sans);font-weight:700;font-size:21px;letter-spacing:-.015em}
.dealsplit__head a{font-size:13.5px;font-weight:700;color:var(--ink);white-space:nowrap;border-bottom:1px solid rgba(0,0,0,.25);padding-bottom:1px}
.dealsplit__head a:hover{border-bottom-color:var(--ink)}
/* On the accent band the cards lose their hairline — the band is the frame. */
.dealsplit .grid-3--cards > .pcard,
.dealsplit .grid-3--cards > .pcard:nth-child(3n){border:0}
.dealsplit .grid-3--cards{gap:14px}

/* ---- .pcard in discount mode -------------------------------------------
   Two extra slots on the ONE product card, nothing else. They are inert on
   every other surface: card-product.php only fills them when `discount` is
   passed AND the product really has an active promo below its list price. */
.pcard__off{position:absolute;top:10px;left:10px;z-index:2;pointer-events:none;background:var(--neg);color:#fff;font-family:var(--sans);font-size:11.5px;font-weight:800;letter-spacing:.04em;padding:4px 9px}
.pcard__was{font-family:var(--sans);font-size:var(--fs-md);font-weight:500;color:var(--mute);margin-left:8px;letter-spacing:0}
/* Compact deal card: shorter image, no summary line, tighter body. Same
   anatomy, same classes — density only, so a deal card and a test card are
   visibly the same object. */
.pcard--compact .pcard__media{height:130px}
.pcard--compact .pcard__body{padding:13px 15px 15px;gap:7px}
.pcard--compact .pcard__title{font-size:var(--fs-lg)}
.pcard--compact .pcard__score b{font-size:var(--fs-2xl)}
/* --fs-2xl (18px), was a raw 19px: its two siblings in this block are already
   tokenised (--fs-lg title, --fs-2xl score), so the price was the one holdout. */
.pcard--compact .pcard__price{font-size:var(--fs-2xl)}
.pcard--compact .pcard__cta{padding:10px;font-size:13.5px}

/* ---- brand tiles --------------------------------------------------------- */
.btiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
.btiles--3{grid-template-columns:repeat(3,minmax(0,1fr))}
/* --hair-soft, not --hair (2026-08-21): the live token overrides set --hair to
   #ffffff, and the only reason this tile looked like a card at all was that
   .sm-rab re-scopes --hair to #d5dde1 for the Rabatt family. Reused outside that
   wrapper — the search results now render brand hits as .btile — the border went
   white and the card lost its edge entirely. --hair-soft is the literal the
   token block keeps for exactly this, so the component no longer depends on an
   ambient override to be visible. Inside .sm-rab this is a #d5dde1 -> #dfe1e6
   shift, i.e. invisible. */
.btile{background:var(--card);border:1px solid var(--hair-soft);padding:18px 18px 16px;display:flex;flex-direction:column;gap:8px;color:var(--ink);transition:border-color .15s,background .15s}
.btile:hover{border-color:var(--ink);background:var(--card)}
.btile__id{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.btile__logo{flex:none;width:72px;height:22px;object-fit:contain;object-position:left center;display:block}
.btile__id b{font-family:var(--sans);font-weight:700;font-size:var(--fs-xl);line-height:1.2;letter-spacing:-.015em}
.btile__note{font-size:12.5px;color:var(--mute)}
.btile__best{font-size:12.5px;font-weight:700;color:var(--hi-2);margin-top:2px}
/* A brand tile in the SEARCH results grid (index.php) sits in a row beside
   product and article cards that are three times its height. Without this it
   stretches to the row and becomes a small block of text floating in a tall
   empty box — the shape this replaced. It keeps its own height instead. */
.grid-3--cards > .btile{align-self:start}

/* ---- Kategorie-Kacheln (weitere Kategorien) ------------------------------ */
.kattiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.kattile{background:var(--sm-fill);border:1px solid var(--hair);padding:18px 18px 16px;display:flex;flex-direction:column;gap:8px;color:var(--ink);transition:border-color .15s}
.kattile:hover{border-color:var(--ink)}
.kattile__k{display:inline-flex;align-items:center;gap:7px;font-size:10.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--mute)}
.kattile__k em{width:7px;height:7px;border-radius:50%;background:var(--neg);flex:none;font-style:normal}
.kattile b{font-family:var(--sans);font-weight:700;font-size:var(--fs-xl);line-height:1.2;letter-spacing:-.015em}
.kattile__sub{font-size:12.5px;color:var(--mute)}
.kattile__best{font-size:12.5px;font-weight:700;color:var(--hi-2);margin-top:2px}

/* ---- "So prüfen wir" — one component, three surfaces --------------------- */
.pruef__k{font-size:var(--fs-2xs);font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--mute);margin:0 0 12px}
.pruef__grid{display:grid;gap:22px}
.pruef__item{display:flex;flex-direction:column;gap:5px;min-width:0}
.pruef__item b{font-size:15.5px;font-weight:700}
.pruef__item small{font-size:13.5px;line-height:1.55;color:var(--soft)}
.pruef__note{font-size:var(--fs-xs);line-height:1.55;color:var(--mute);margin:12px 0 0}
/* ONE surface, all three pages. It briefly had three (white strip on the hub,
   sidebar card on the category page, accent band on the brand page) because
   the prototypes drew it three ways — but it is the same promise in the same
   words every time, and a reader who meets it twice should recognise it. */
.pruef{background:var(--card);border:1px solid var(--hair);padding:22px 24px}
.pruef .pruef__grid{grid-template-columns:repeat(3,minmax(0,1fr))}

/* ---- brand page: main + open sidebar rail ------------------------------- */
.rab-main{display:grid;grid-template-columns:minmax(0,1fr) 300px;column-gap:44px;row-gap:26px;align-items:start}
.rab-main > .rab-col{min-width:0}
.rab-side{min-width:0;align-self:stretch}
.rab-side__in{position:sticky;top:calc(var(--sm-rab-top) + 20px)}
/* The OPEN style, not the enclosed card: a hairline rail, no box. */
.brail{border-left:1px solid var(--hair);padding:4px 0 4px 26px;display:flex;flex-direction:column;gap:24px}
.brail__top{display:flex;align-items:center;justify-content:space-between;gap:12px}
.brail__lock{display:inline-flex;align-items:center;gap:8px;color:var(--ink);min-width:0}
.brail__lock img{flex:none;width:auto;height:24px;max-width:96px;object-fit:contain;display:block}
.brail__lock b{font-family:var(--sans);font-weight:800;font-size:var(--fs-xl);letter-spacing:-.01em}
.brail__site{font-size:12.5px;font-weight:700;color:var(--hi-2);white-space:nowrap}
.brail__site:hover{color:var(--ink)}
.brail__best{display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center}
.brail__pct{font-family:var(--serif);font-weight:800;font-size:clamp(40px,4vw,54px);line-height:1}
.brail__tag{background:var(--hi);color:var(--ink);font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:5px 11px}
.brail__code .dealcode--peek{margin:0}
/* The rail's verification stamp: same treatment as the prop labels right below
   it (.brail__prop small — --fs-xs, --mute, normal weight), NOT the uppercase
   letter-spaced stamp it briefly was (Tim, 2026-08-21: "too aggressive and
   capitalized"). It is a caption under the code widget, so it should sit back;
   the pulsing dot is what carries the "live" signal. */
.brail__code .chkline{justify-content:center;margin-top:10px;width:100%;font-size:var(--fs-xs);font-weight:400;letter-spacing:0;text-transform:none;color:var(--mute);gap:7px}
.brail__code .chkline__dot{width:6px;height:6px}
.brail__props{border-top:1px solid var(--hair);padding-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:16px 12px}
.brail__prop{display:flex;align-items:flex-start;gap:10px;min-width:0}
.brail__prop svg{flex:none;width:20px;height:20px;margin-top:1px;stroke:var(--hi-2);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.brail__prop span{display:flex;flex-direction:column;gap:1px;min-width:0}
.brail__prop b{font-size:13.5px;font-weight:700;line-height:1.3}
.brail__prop small{font-size:var(--fs-xs);color:var(--mute);line-height:1.35}

/* ---- brand page: filter chips above the list, expired archive ------------ */
.ofilters{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:18px}
.ofilters .sm-chip{color:#1a2740;font-weight:700}
/* The chip's pressed state is the theme's own `is-on` fill — the archive
   filter bar's chips look exactly like this, which is the whole point. */
.sm-chip[aria-pressed="true"]{background:var(--hi);border-color:transparent}
.sm-chip[aria-pressed="true"] small{color:var(--ink);opacity:.7}
.expired-toggle{margin-top:18px}
/* The pill IS the summary, so it needs the family's +/– instead of the native
   disclosure triangle sitting inside a rounded button (2026-08-23). Same
   indicator as .offer__terms and .item__more. */
.expired-toggle>summary{list-style:none;display:inline-flex;align-items:center;gap:10px}
.expired-toggle>summary::-webkit-details-marker{display:none}
.expired-toggle>summary::after{content:"+";font-size:var(--fs-md);font-weight:800;line-height:1;color:var(--hi-2)}
.expired-toggle[open]>summary::after{content:"\2013"}
/* --hair-soft, not --hair: same override story as .pillbtn above — these are
   the archive's only dividers, and at #ffffff the rows ran into each other. */
.expired-list{margin-top:14px;border-top:1px solid var(--hair-soft)}
.expired-list__row{display:flex;align-items:baseline;gap:16px;padding:13px 2px;border-bottom:1px solid var(--hair-soft);opacity:.62}
.expired-list__row b{font-family:var(--sans);font-weight:700;font-size:var(--fs-xl);width:72px;flex:none;letter-spacing:-.015em}
.expired-list__row span{font-size:var(--fs-base);flex:1 1 auto;min-width:0}
.expired-list__row time{font-size:var(--fs-xs);color:var(--mute);white-space:nowrap}

/* ---- newsletter insert inside the brand offer list ----------------------- */
/* margin (2026-08-21): the insert sits BETWEEN two `.offers` stacks, and each
   stack spaces its own rows with `gap:14px` but carries no outer margin — so
   the box was flush against the row above and the row below, reading as part
   of the list rather than as an insert. 14px on both sides puts it in the same
   rhythm as every other gap in the feed. */
.nlbox{background:var(--hi);padding:20px 22px;margin:14px 0;display:flex;align-items:center;gap:20px;flex-wrap:wrap}
.nlbox__txt{display:flex;flex-direction:column;gap:4px;flex:1 1 250px;min-width:0}
.nlbox__txt b{font-family:var(--sans);font-weight:700;font-size:21px;line-height:1.15;letter-spacing:-.015em}
.nlbox__txt small{font-size:13.5px;color:var(--ink);opacity:.72}
.nlbox__cta{flex:1 1 240px;display:flex;justify-content:flex-end}

/* ---- brand page: the "im Test" grid keeps the shared card ----------------
   Full-width row below the rail grid (2026-08-20) — 3-up here renders the
   shared .pcard at the catalog's ~350px width. (While the grid lived inside
   the narrow content column, 3-up squeezed cards to ~240px, which read as a
   different card skin.) */
.rab-tests{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px}
/* No-code sidebar CTA: the standard button, block-level in the rail. */
.brail__shop{display:block;width:100%;text-align:center;box-sizing:border-box}
.brail__code .anz{display:block;text-align:center;margin-top:6px}

/* ---- responsive ---------------------------------------------------------- */
@media (max-width: 1080px){
  .btiles{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (max-width: 980px){
  .rab-main{grid-template-columns:minmax(0,1fr)}
  .rab-side__in{position:static}
  .brail{border-left:0;border-top:1px solid var(--hair);padding:22px 0 0}
  /* (3) The brand rail carries the best code, the percentage and the brand
     props — the reason the page was opened. Stacked, it belongs directly under
     the head, not below the FAQ 6000px away. Only the --rail grid reorders:
     the other .rab-main uses (prose + facts table, FAQ + method) must keep
     their source order. */
  .rab-main--rail > .rab-side{order:-1}
  .rab-main--rail > .rab-side .brail{border-top:0;padding-top:0;padding-bottom:26px;border-bottom:1px solid var(--hair);margin-bottom:8px}
  .rab-tests{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
  .kattiles{grid-template-columns:1fr 1fr}
}
@media (max-width: 880px){
  .btiles,.btiles--3{grid-template-columns:1fr 1fr}
  .cover > .cover__in{grid-template-columns:minmax(0,1fr);gap:16px;padding:26px 16px}
  .cover__cta{text-align:center}
  .pruef .pruef__grid{grid-template-columns:1fr 1fr}
}
@media (max-width: 760px){
  /* No .sm-rab-only reflow of .sec__head here on purpose (Tim, 2026-08-23):
     the rabatte pages used to dissolve the head on phones and drop its link
     below the list. That was a private layout no other template had, so the
     head now wraps exactly like it does everywhere else. */
  /* The blue product band on the hub keeps its own reflow: .dealsplit is a
     component inside the feed, not a section head. */
  .dealsplit{display:flex;flex-direction:column}
  .dealsplit__head{display:contents}
  .dealsplit__head b{order:1;margin-bottom:14px}
  .dealsplit .grid-3--cards{order:50}
  .dealsplit__head a{order:99;align-self:flex-start;margin-top:16px}

  .offer{grid-template-columns:minmax(0,1fr)}
  .offer__val{flex-direction:row;align-items:baseline;justify-content:flex-start;gap:8px;padding:14px 18px;text-align:left}
  .offer__cta{border-left:0;border-top:1px solid var(--hair)}
  .offer__strip{padding:9px 18px}
  .offers__dupes > summary{padding:11px 18px}
  .btiles,.btiles--3,.kattiles{grid-template-columns:minmax(0,1fr)}
  .pruef .pruef__grid{grid-template-columns:minmax(0,1fr);gap:16px}
  .pruef{padding:18px}
  .dealsplit{padding:18px}
  .rhead > .wrap{padding-top:14px;padding-bottom:28px}
  .chkrow .chkline{margin-left:0}
  .rsearch .nav-search--hub{height:50px;padding-left:14px}
  .rsearch .nav-search__btn{padding:0 16px}
  .expired-list__row{flex-wrap:wrap;gap:6px 14px}
  .expired-list__row span{flex:1 1 100%;order:2}
  .nlbox__cta{justify-content:flex-start}
}

/* ============ Test-/Review archive — head, filter bar, results ============
   The category listing a reader lands on from the nav ("Naturlatexmatratzen",
   "Beste Topper"). Replaces the old .catalog facet SIDEBAR with a sticky
   filter/sort bar above a gapped card grid (design handoff, 2026-08).

   Token scope: production overrides --hair to #ffffff, --radius to 20px and
   --serif to IBM Plex Sans from Design > Design tokens. This template is drawn
   in hairlines, square corners and Playfair, so it re-declares those three on
   its own scope and inherits everything else (ink, soft, mute, hi, hi-2, dark,
   paper, card) from the live global palette. Same deal as .pcard — remove both
   scopes together if the global tokens are ever realigned. */
/* .sm-fbar is listed alongside .sm-arch because the full product archive
   (archive.php) uses the bar WITHOUT the .sm-arch page head — it would
   otherwise inherit neither the hairline nor the sticky offset. */
.sm-arch,.sm-fbar{
  --hair:#d5dde1;
  --radius:0;
  --serif:"Playfair Display", Georgia, serif;
  /* The prototype used --sm-header-h; the theme's nav JS publishes --nav-h and
     --subnav-h instead (initNavHeight). Consume those — never re-measure. */
  --sm-arch-top:calc(var(--nav-h, 70px) + var(--subnav-h, 0px));
  /* Two literals the handoff asked to become tokens rather than inline values. */
  --sm-track:#e4ecf1;
  --sm-deal-band:#dcecf7;
}

/* ---- page head ---- */
.sm-arch__head{background:var(--card);border-bottom:1px solid var(--hair)}
/* DIRECT CHILD, not descendant: the breadcrumb nests its own .wrap inside this
   band, so `.sm-arch__head .wrap` also matched THAT and gave the trail an extra
   18px/26px of padding — a 65px-tall breadcrumb. */
.sm-arch__head > .wrap{padding-top:18px;padding-bottom:26px}
/* The trail sits INSIDE this band (crumbs--bare), so it needs no rule of its
   own — it inherits --sm-crumb/--sm-crumb-strong defaults for a light band. */
.sm-arch__head .crumbs{margin-bottom:14px}
.sm-arch__title{font-family:var(--serif);font-weight:800;font-size:42px;line-height:1.06;letter-spacing:-.012em;margin:0 0 12px}
.sm-arch__lead{font-size:16.5px;line-height:1.6;color:var(--soft);margin:0;max-width:62ch}
.sm-arch__stamp{margin-top:14px;font-size:12.5px;color:var(--mute)}

/* ---- sticky filter + sort bar ----
   The expanded "Alle Filter" panel lives INSIDE this wrapper so it pins with
   the bar. Deliberately not position:fixed anywhere. */
.sm-fbar{position:sticky;top:var(--sm-arch-top);z-index:var(--z-fbar);background:var(--card);border-bottom:1px solid var(--hair)}
/* Every .wrap-based row in this bar is scoped to an ancestor (0,2,0) rather
   than left at (0,1,0). The responsive `.wrap{padding:0 16px}` shorthand lives
   LATER in this file, so an unscoped `.sm-fbar__row{padding-top:…}` lost to it
   below 760px and the whole bar collapsed to zero vertical padding. */
.sm-fbar .sm-fbar__row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding-top:14px;padding-bottom:14px}
.sm-fbar__chips{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
.sm-fbar__slot{position:relative;flex:none}

/* chips — single-select each; selected swaps to the soft fill and drops its border */
.sm-chip{display:inline-flex;align-items:center;gap:8px;font-family:var(--sans);font-size:14.5px;font-weight:600;padding:9px 15px;border-radius:999px;border:1px solid var(--hair);background:var(--card);color:var(--ink);cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}
.sm-chip__caret{font-size:9px;opacity:.5}
.sm-chip.is-on{background:var(--hi);border-color:transparent}
.sm-chip:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}

/* chip popover (desktop only — mobile uses the native select layered over it)
   Option lists are derived from the catalogue and get LONG — a mattress
   category currently offers 37 sizes and 20 brands, which unchecked runs about
   1400px past the fold. The cap below is the no-JS floor; initProductFilter
   then measures the real distance to the viewport bottom and sets an exact
   max-height, so the list always ends on screen at any scroll position.
   overscroll-behavior stops the page scrolling on behind it at the ends. */
.sm-pop{position:absolute;top:calc(100% + 8px);left:0;z-index:60;min-width:244px;background:var(--card);border:1px solid var(--hair);border-radius:12px;box-shadow:0 14px 34px rgba(17,35,47,.14);padding:7px;display:flex;flex-direction:column;max-height:min(58vh,440px);overflow-y:auto;overscroll-behavior:contain}
/* Two affordances, because a capped 36-item list must not read as a 12-item one:
   1. the scrollbar thumb is --mute, not --hair (at hairline weight it vanishes
      against the white popover), though Chrome only reserves ~2px for it;
   2. a scroll shadow at whichever edge still has content past it. The two
      `local` gradients paint the card colour and scroll WITH the content, so
      they uncover the two `scroll` radial shadows only while more remains —
      no JS, and it turns itself off at both ends. */
.sm-pop{scrollbar-width:thin;scrollbar-color:var(--mute) transparent}
.sm-pop{
  background:
    linear-gradient(var(--card) 30%, rgba(255,255,255,0)) top    / 100% 26px no-repeat local,
    linear-gradient(rgba(255,255,255,0), var(--card) 70%) bottom / 100% 26px no-repeat local,
    radial-gradient(farthest-side at 50% 0,   rgba(17,35,47,.14), rgba(17,35,47,0)) top    / 100% 11px no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, rgba(17,35,47,.14), rgba(17,35,47,0)) bottom / 100% 11px no-repeat scroll,
    var(--card);
}
.sm-pop::-webkit-scrollbar{width:10px}
.sm-pop::-webkit-scrollbar-track{background:transparent}
.sm-pop::-webkit-scrollbar-thumb{background:var(--mute);border-radius:999px;border:3px solid var(--card)}
.sm-pop::-webkit-scrollbar-thumb:hover{background:var(--soft)}
/* Opens upward when there is more room above than below (short viewports, or a
   chip near the fold before the bar pins). Set by initProductFilter. */
.sm-pop--up{top:auto;bottom:calc(100% + 8px)}
/* Option rows must not shrink to fit a capped popover — they scroll instead. */
.sm-pop__opt{flex:none}
.sm-pop--right{left:auto;right:0;min-width:224px}
.sm-pop__opt{display:flex;align-items:center;gap:10px;width:100%;border:0;background:none;border-radius:8px;padding:9px 11px;cursor:pointer;font-family:var(--sans);font-size:14.5px;font-weight:500;color:var(--ink);text-align:left}
.sm-pop__opt:hover{background:var(--paper)}
.sm-pop__opt.is-on{background:var(--paper);font-weight:700}
/* :not(.sm-pop__mark) is load-bearing. The option is a flex row of two spans,
   label + checkmark, and a bare `.sm-pop__opt span` (0,1,1) also matched the
   mark and outranked `.sm-pop__mark{flex:none}` (0,1,0) — so the ✓ span, empty
   on every unselected row, took flex:1 and half the width. Measured on the
   Sortieren menu: label 88px, mark 88px, and "Preis aufsteigend" wrapping to two
   lines in a 224px menu whose content only wants 162px. */
.sm-pop__opt > span:not(.sm-pop__mark){flex:1;min-width:0}
.sm-pop__mark{flex:none;font-size:13px;color:var(--hi-2)}

/* The real <select> layered over a chip: invisible, but it is what a tap opens
   on mobile. 16px font-size is load-bearing — anything smaller zooms iOS. */
.sm-fnative{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:0;background:none;font-size:16px;-webkit-appearance:none;appearance:none;display:none;z-index:3}

/* "Alle Filter" */
.sm-fbtn{display:inline-flex;align-items:center;gap:9px;font-family:var(--sans);font-size:14.5px;font-weight:700;padding:9px 15px;border-radius:999px;border:1px solid var(--ink);background:var(--card);color:var(--ink);cursor:pointer;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}
.sm-fbtn.is-on{background:var(--ink);color:#fff;border-color:transparent}
.sm-fbtn__gl{display:inline-flex;flex-direction:column;gap:2px}
.sm-fbtn__gl i{display:block;height:1.5px;background:currentColor}
.sm-fbtn__gl i:nth-child(1){width:13px}
.sm-fbtn__gl i:nth-child(2){width:9px}
.sm-fbtn__gl i:nth-child(3){width:5px}
.sm-fbtn__badge{display:inline-flex;align-items:center;justify-content:center;min-width:19px;height:19px;padding:0 5px;border-radius:999px;background:var(--hi-2);color:#fff;font-size:11.5px;font-weight:700}
/* Any author `display` beats the UA stylesheet's [hidden]{display:none}, so
   every element this template toggles with the hidden ATTRIBUTE needs its own
   guard or it never actually hides. Same reason .pcard/.sm-fpanel/.sm-feat
   carry one. */
.sm-fbtn__badge[hidden]{display:none}
.sm-fbtn:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}

/* right cluster: live result count + sort */
.sm-fbar__right{margin-left:auto;display:flex;align-items:center;gap:14px;flex:none}
.sm-fbar__count{font-size:13.5px;color:var(--mute);white-space:nowrap}
.sm-sort{display:inline-flex;align-items:center;gap:9px;border:1px solid var(--hair);background:var(--card);color:var(--ink);font-family:var(--sans);font-size:14.5px;font-weight:600;padding:9px 15px;border-radius:999px;cursor:pointer;white-space:nowrap;transition:border-color .15s}
.sm-sort:hover{border-color:var(--ink)}
.sm-sort__lbl{color:var(--mute);font-weight:500}
.sm-sort b{font-weight:700}
.sm-sort__caret{font-size:9px;opacity:.5}
.sm-sort:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}

/* ---- row 2: active filters (only rendered when something is selected) ---- */
.sm-fbar .sm-active{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-bottom:14px}
.sm-fbar .sm-active[hidden]{display:none}
.sm-active__lbl{font-family:var(--serif);font-size:15px;font-weight:700;color:var(--ink);margin-right:4px}
.sm-active__chip{display:inline-flex;align-items:center;gap:8px;background:var(--hi);color:var(--ink);border:0;font-family:var(--sans);font-size:13.5px;font-weight:600;padding:7px 12px;border-radius:999px;cursor:pointer;transition:background .15s,color .15s}
.sm-active__chip:hover{background:var(--hi-2);color:#fff}
.sm-active__x{font-size:14px;line-height:1;opacity:.65}
.sm-linkbtn{background:none;border:0;font-family:var(--sans);font-size:13.5px;font-weight:700;color:var(--ink);cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color .15s;padding:0}
.sm-linkbtn:hover{color:var(--hi-2)}
.sm-active .sm-linkbtn{margin-left:4px}

/* ---- "Alle Filter" panel ---- */
.sm-fpanel{border-top:1px solid var(--hair);background:#fcfdfe}
.sm-fpanel[hidden]{display:none}
.sm-fpanel .sm-fpanel__grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:26px 34px;padding-top:26px;padding-bottom:22px}
.sm-fgroup{min-width:0}
/* Sentence case, NOT uppercase micro-caps — deliberate, see the handoff. */
.sm-fgroup__lbl{font-size:12.5px;font-weight:700;letter-spacing:.04em;color:var(--ink);margin-bottom:11px}
.sm-fgroup__opts{display:flex;flex-wrap:wrap;gap:7px}
.sm-fpill{border:1px solid var(--hair);background:var(--card);color:var(--ink);font-family:var(--sans);font-size:13.5px;font-weight:600;padding:7px 13px;border-radius:999px;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s}
.sm-fpill.is-on{background:var(--hi);border-color:transparent}
.sm-fpill:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}
/* vegan switch */
.sm-switch{display:flex;align-items:center;gap:11px;background:none;border:0;padding:4px 0;cursor:pointer;font-family:var(--sans);font-size:14.5px;font-weight:600;color:var(--ink)}
.sm-switch__track{position:relative;display:inline-block;width:38px;height:22px;border-radius:999px;flex:none;background:#d9e2e8;transition:background .15s}
.sm-switch__knob{position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(17,35,47,.3);transition:left .15s}
.sm-switch.is-on .sm-switch__track{background:var(--hi-2)}
.sm-switch.is-on .sm-switch__knob{left:19px}
.sm-fpanel__footwrap{border-top:1px solid var(--hair);background:var(--card)}
.sm-fpanel .sm-fpanel__foot{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding-top:14px;padding-bottom:14px}
.sm-fapply{margin-left:auto;background:var(--hi);color:var(--ink);border:0;font-family:var(--sans);font-size:15px;font-weight:700;padding:12px 24px;border-radius:12px;cursor:pointer;transition:background .15s,color .15s}
.sm-fapply:hover{background:var(--hi-2);color:#fff}

/* ---- results grid ----
   padding-top is the breathing room between the sort row and the first card's
   top border; the sticky bar has no shadow, so without it the two rules touch. */
.sm-arch .sm-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px;padding-top:36px;padding-bottom:80px}
.sm-arch__empty{padding:60px 0;text-align:center;font-weight:600;color:var(--soft)}

/* ---- featured "star product" block (Customizer: sm_archive_featured) ----
   Spans the grid, only under Relevanz sort. Content comes from the category's
   star product (sm_star_for_term) — the same flag the mega menu reads. */
.sm-feat{grid-column:1 / -1;background:var(--dark);color:#fff;display:grid;grid-template-columns:minmax(0,320px) minmax(0,1fr);align-items:stretch}
.sm-feat[hidden]{display:none}
.sm-feat__media{position:relative;min-height:250px;background:#16242f}
.sm-feat__media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.sm-feat__stamp{position:absolute;top:14px;left:14px;background:var(--hi);color:var(--ink);font-size:10.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;padding:6px 12px}
.sm-feat__body{padding:26px 28px;display:flex;flex-direction:column;gap:12px;min-width:0}
.sm-feat__kicker{font-size:10.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--hi)}
.sm-feat__head{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;flex-wrap:wrap}
.sm-feat__id{min-width:0;display:flex;flex-direction:column;gap:5px}
.sm-feat__brand{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--on-dark-muted,#b3c0c8)}
.sm-feat__title{font-family:var(--serif);font-weight:800;font-size:30px;line-height:1.1;margin:0}
.sm-feat__title a{color:inherit}
.sm-feat__score{flex:none;display:flex;align-items:baseline;gap:4px}
.sm-feat__score b{font-family:var(--serif);font-weight:800;font-size:40px;line-height:1}
.sm-feat__score small{font-size:14px;color:var(--on-dark-muted,#b3c0c8)}
.sm-feat__text{font-size:15.5px;line-height:1.6;color:var(--on-dark-muted,#b3c0c8);margin:0;max-width:58ch}
.sm-feat__foot{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:auto;padding-top:14px}
.sm-feat__price{font-family:var(--serif);font-weight:800;font-size:26px}
.sm-feat__spec{font-size:12.5px;color:var(--on-dark-muted,#b3c0c8)}
/* On the dark band the inverse pairing is the one in the book (.btn--hi):
   soft fill + ink, going to the card surface on hover. No bare #fff literal. */
.sm-feat__cta{margin-left:auto;display:inline-flex;align-items:center;gap:9px;background:var(--hi);color:var(--ink);font-family:var(--sans);font-size:15px;font-weight:700;padding:13px 22px;transition:background .15s,color .15s}
.sm-feat__cta:hover{background:var(--card);color:var(--ink)}
.sm-feat__cta:focus-visible{outline:3px solid var(--hi);outline-offset:3px}
/* The dark band re-points the global breadcrumb variables at itself — this is
   the whole reason .crumbs reads them instead of hard-coding --mute/--ink. */
.sm-feat{--sm-crumb:rgba(255,255,255,.72);--sm-crumb-strong:#fff}

@media (max-width: 1080px){
  .sm-arch .sm-grid{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
}
@media (max-width: 900px){
  .sm-fpanel .sm-fpanel__grid{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
}
@media (max-width: 760px){
  .sm-arch__head > .wrap{padding-top:14px;padding-bottom:22px}
  .sm-arch__head .crumbs{margin-bottom:12px}
  .sm-arch__title{font-size:clamp(28px,8vw,38px)}
  /* Mobile drops BOTH rules that frame the bar: the page head's bottom edge
     sits directly above it and the bar's own bottom edge directly below, so at
     this width the filter row was boxed in by two hairlines that carry no
     information. The head/bar boundary is already read from the change of
     rhythm. Desktop keeps them — there the bar is a distinct band. */
  .sm-arch__head{border-bottom:0}
  .sm-fbar{border-bottom:0}
  .sm-fbar .sm-fbar__row{padding-top:12px;padding-bottom:12px;row-gap:12px}
  /* one horizontally scrolling strip, scrollbar hidden */
  .sm-fbar__chips{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;width:100%;padding-bottom:2px}
  .sm-fbar__chips::-webkit-scrollbar{display:none}
  .sm-fbar__chips > *{flex:none}
  .sm-fbar__right{width:100%;margin-left:0;justify-content:space-between}
  /* native OS pickers replace the custom popovers below 760px */
  .sm-pop{display:none !important}
  .sm-fnative{display:block}
  /* the panel scrolls internally and pins its footer — never position:fixed */
  .sm-fpanel{background:var(--card);display:flex;flex-direction:column;max-height:calc(100vh - var(--sm-arch-top) - 210px)}
  .sm-fpanel .sm-fpanel__grid{grid-template-columns:minmax(0,1fr);overflow-x:hidden;padding-top:20px;padding-bottom:18px;gap:22px 20px;flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
  .sm-fpanel__footwrap{flex:none;box-shadow:0 -6px 18px rgba(17,35,47,.08)}
  .sm-fpanel .sm-fpanel__foot{justify-content:space-between;padding-top:12px;padding-bottom:12px}
  .sm-fbar .sm-active{padding-bottom:12px}
  .sm-arch .sm-grid{padding-top:24px;padding-bottom:60px;gap:14px}
  .sm-feat{grid-template-columns:minmax(0,1fr)}
  .sm-feat__media{min-height:200px}
  .sm-feat__body{padding:22px 18px}
  .sm-feat__title{font-size:26px}
  .sm-feat__cta{margin-left:0;width:100%;justify-content:center}
}

/* ============ NAV VERSION B — three-tier bar ============ */
.nav-b .nav-util{background:var(--dark);border-bottom:var(--frame) solid var(--ink)}
.nav-b .nav-util__row{display:flex;align-items:center;justify-content:space-between;gap:18px;min-height:38px}
.nav-b .nav-util__promo{font-size:var(--fs-xs);font-weight:600;letter-spacing:.02em;color:var(--on-dark);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-b .nav-util__promo a{color:var(--on-dark)}
.nav-b .nav-util__promo a:hover{color:var(--hi)}
.nav-b .nav-util__tools{display:flex;align-items:center;gap:12px;flex:none}
.nav-b .nav-util__rabatte{font-size:var(--fs-xs);font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--hi)}
.nav-b .nav-util__rabatte:hover{color:#fff;box-shadow:inset 0 -2px var(--hi)}
/* Field fill: the handoff (design_handoff_produktseite §2) asks for a light
   grey field and names --paper for it. Production overrides --paper to pure
   #fff (Design > Design tokens), which would make "light grey" invisible, so
   the fill is drawn with --hair-soft — the token that exists precisely because
   it survives the --paper / --hair overrides. Border and everything else stay. */
.nav-search{display:flex;align-items:stretch;border:var(--frame) solid var(--ink);background:var(--hair-soft);height:30px}
.nav-b .nav-util .nav-search{height:28px}
.nav-search__input{border:0;background:transparent;padding:0 10px;font-family:var(--sans);font-size:var(--fs-sm);color:var(--ink);width:160px;min-width:0}
/* no inner outline on the input — the focus cue is the box border darkening */
.nav-search__input:focus{outline:none}
.nav-search:focus-within{border-color:var(--ink)}
.nav-search__btn{flex:none;display:grid;place-items:center;width:32px;border:0;border-left:var(--frame) solid var(--ink);background:var(--hi);color:var(--ink);cursor:pointer}
.nav-search__btn:hover{background:var(--ink);color:#fff}
.nav-search__btn svg{width:15px;height:15px;display:block}
.nav-b .nav__row{gap:20px}
.nav-cta{flex:none;display:inline-flex;align-items:center;font-family:var(--sans);font-weight:800;font-size:var(--fs-sm);letter-spacing:.06em;text-transform:uppercase;color:var(--ink);background:var(--hi);border:var(--frame) solid var(--ink);padding:9px 16px;box-shadow:var(--shadow-2);transition:transform .12s,box-shadow .12s,background .12s}
.nav-cta:hover{background:var(--ink);color:#fff;transform:translate(1px,1px);box-shadow:var(--shadow-1)}
.nav-b .nav-catstrip{background:var(--paper);border-top:var(--frame) solid var(--line);border-bottom:var(--frame) solid var(--line)}
.nav-b .nav-catstrip__row{display:flex;gap:0;overflow-x:auto;-webkit-overflow-scrolling:touch}
.nav-b .nav-catstrip a{flex:none;padding:10px 16px;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--soft);border-right:var(--frame-hair) solid var(--hair);white-space:nowrap}
.nav-b .nav-catstrip a:hover{background:var(--ink);color:#fff}
/* .nav-search--mobile is gone with the in-flow sheet (2026-08-19): the mobile
   search is its own level in the modal now, styled as .nav-search--full. */
/* dark rule between the search field and the first menu row (design) */
.mobile-nav .nav-search--full + .mnav-group,.mobile-nav .nav-search--full + a{border-top:var(--frame) solid var(--line)}
@media (max-width: 980px){
  .nav-b .nav__menu{display:none}
  .nav-b .nav-cta{display:none}
  .nav-b .nav-catstrip{display:none}
  .nav-b .nav-util .nav-search{display:none}
  .nav-b .nav-util__row{justify-content:flex-end}
  .nav-b .nav-util__promo{display:none}
}
@media (max-width: 620px){
  .nav-b .nav-util{display:none}
  .nav-b .nav__row{gap:12px}
}

/* =========================================================================
   RESPONSIVE — full pass
   ========================================================================= */

/* ---- global mobile safety (all widths) ---- */
html, body { overflow-x: clip; }                       /* clip, not hidden: keeps .nav sticky working */
img { height: auto; }                                   /* WP imgs carry width/height attrs */
iframe, video, embed, object { max-width: 100%; }
.tbl-scroll { -webkit-overflow-scrolling: touch; max-width: 100%; }
.subnav__row { -webkit-overflow-scrolling: touch; flex-wrap: nowrap; }

/* ---- ≤980px: mobile nav, sidebar collapse, tablet grids, footer ---- */
@media (max-width: 980px) {
  .nav__links { display: none; }
  .nav__menu { display: none; }
  .mega-grid { grid-template-columns: 1fr 1fr; }
  .nav-burger { display: flex; }
  /* A fixed full-screen layer: nothing to un-stick, nothing to scroll to.
     The old in-flow sheet needed `.nav{position:static}` and a scrollTo(0,0)
     to be reachable at all; both are gone with it. */
  .mobile-nav.is-open { display: flex; }
  /* The modal lives INSIDE <header class="nav">, which is its own stacking
     context at --z-nav (30). So --z-overlay only ranks the modal WITHIN the
     header, and the promo topbar at --z-topbar (31) painted straight over the
     modal's Back button — visible, but not clickable. Lift the whole header
     while the menu is open; the topbar goes back on top the moment it closes. */
  /* Coupled to --z-searchmodal: anything that must cover the OPEN burger menu
     is competing with this 1000, not with --z-overlay. */
  .nav:has(.mobile-nav.is-open) { z-index: var(--z-toast); }
  /* mobile keeps the magnifier + Enter to submit; the text button goes */
  .nav-search__btn--txt { display: none; }
  .mega-wrap { display: none !important; }
  .with-aside { grid-template-columns: 1fr; gap: 40px; }
  .aside { position: static; max-width: none; }
  /* The JS (initAsideReflow) moves .aside-wrap INTO the main column after
     #testergebnis on phones — inside the column the grid gap no longer
     separates it, so it carries its own vertical margins. */
  .main-col > .aside-wrap { margin: 6px 0 44px; }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 > *:nth-child(3n) { border-right: var(--frame) solid var(--line); }
  .grid-3 > *:nth-child(2n) { border-right: 0; }
  .foot__grid { grid-template-columns: 1fr 1fr; gap: 32px; }
  .hero { padding: 64px 0 56px; }
}

/* ---- ≤880px: hero grid, comparison item, coupon ---- */
@media (max-width: 880px) {
  .hero__grid { grid-template-columns: 1fr; gap: 36px; }
  .item { grid-template-columns: 1fr; gap: 14px; padding: 38px 0; }
  .item__no { font-size: 64px; }
  .item__body { grid-template-columns: 1fr; gap: 28px; }
  /* Stacked: lead with the award/brand/product name so the photo below it has
     a label. Source order stays image-first for the desktop two-column look. */
  .item__main { display: flex; flex-direction: column; }
  .item__main > .item__head { order: -1; }
  .right { border-left: 0; border-top: var(--frame) solid var(--line); padding-left: 0; padding-top: 22px; }
  /* stacked: the rail is a normal block under the narrative, nothing to stick to */
  .right__stick { position: static; max-height: none; overflow: visible; padding: 0; margin: 0; }
  .coupon { grid-template-columns: 1fr; }
  .coupon__code { border-left: 0; border-top: var(--frame-bold) solid var(--line); }
}

/* ---- ≤760px: typography step-down, tables, subnav tap targets ---- */
@media (max-width: 760px) {
  .hero { padding: 52px 0 46px; }
  .hero--compact { padding: 40px 0 34px; }
  .hero__lead { font-size: var(--fs-2xl); }
  .hero__meta { gap: 18px 26px; margin-top: 28px; }
  .item h3 { font-size: clamp(24px, 5.4vw, 32px); }
  .item .desc { font-size: var(--fs-lg); }
  .bigscore { font-size: 64px; }
  .bigscore sup { font-size: var(--fs-2xl); }
  .price { font-size: 26px; }
  .subnav a { padding: 14px 15px; font-size: var(--fs-xs); }
  .tbl { font-size: var(--fs-md); }
  .tbl th { padding: 11px 10px; }
  .tbl td { padding: 12px 10px; }
  .spectable th, .spectable td { padding: 12px 14px; font-size: var(--fs-md); }
  .spectable th { width: 38%; }
  .figfull image-slot { height: 240px; }
  .sec__lead { font-size: var(--fs-lg); }
  .deal__main { padding: 18px; }
  .deal__act { padding: 16px 18px; }
}

/* ---- ≤640px: phone layout ---- */
@media (max-width: 620px) {
  :root { --space-sec: 56px; }
  .wrap { padding: 0 20px; }
  .grid-2, .stats, .trustrow { grid-template-columns: 1fr; }
  .trust { border-right: 0; border-bottom: var(--frame) solid var(--line); }
  .trust:last-child { border-bottom: 0; }
  .grid-3 { grid-template-columns: 1fr; }
  /* :nth-child(3n) too, or it wins: the 2-column rule above restores a right
     border on every 3rd cell and out-specifies a bare `.grid-3 > *` (0,2,0 vs
     0,1,0), so at one column every 3rd card kept a vertical line down its right
     edge with nothing beside it. Verified on /blog/ at 500px before this fix. */
  .grid-3 > *,
  .grid-3 > *:nth-child(3n) { border-right: 0; }
  .foot__grid { grid-template-columns: 1fr; }
  .foot { padding: 48px 0 26px; }
  .hero h1 { font-size: 42px; }
  /* ...but 42px is too big for a compact hero carrying a long compound: it put
     "Taschenfederkernmatratze" at 524px against a 440px content width, i.e. 64px
     past the viewport edge at 480px — clipped silently, since nothing here
     scrolls horizontally. The ≤430px block below already steps this down; the
     431-620px band never got the same treatment. Equal specificity to the rule
     above, so it has to come after it. */
  .hero--compact h1 { font-size: 30px; }
  .sec--faq summary { font-size: 19px; padding: 20px 0; }
  .sec--faq details p { font-size: var(--fs-lg); }
  .coupon__main { padding: 26px 20px; }
  .coupon__code { padding: 26px 20px; min-width: 0; }
  .coupon__pct { font-size: 60px; }
  .coupon__pct span { font-size: var(--fs-5xl); }
  .codeval { font-size: var(--fs-4xl); letter-spacing: .04em; text-align: center; }
  .verdict-box { padding: 24px; }
  .verdict-box h3 { font-size: 25px; }
  .verdict-box__score .bigscore { font-size: 56px; }
  .review-score { padding: 22px; }
  .main-col > section + section { margin-top: 44px; }
  .authorbox { padding: 20px; gap: 14px; flex-direction: column; }
}

/* ---- ≤620px: overflow killers + touch targets ---- */
@media (max-width: 620px) {
  .btn { white-space: normal; text-align: center; }
  .stamp { white-space: normal; }
  .hero__meta-item { white-space: normal; }
  .hero__cta { flex-direction: column; align-items: stretch; }
  .hero__cta .btn { width: 100%; }
  .btn--sm { padding: 12px 16px; }
  .btn--lg { padding: 16px 22px; font-size: var(--fs-base); }
  .dealcode__btn { padding: 13px 16px; }
  .codecopy { padding: 13px 18px; }
  .proscons { grid-template-columns: 1fr; gap: 14px; }
  .topbar { font-size: var(--fs-2xs); padding: 8px 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tbl-scroll .tbl { min-width: 640px; }
  .tbl .prod { min-width: 170px; }
  .dealcode__code { font-size: var(--fs-md); letter-spacing: .12em; }
  .scorebars { gap: 14px; }
  .hero__brandlogo { width: 110px; height: 64px; }
  .byline__av { width: 42px; height: 42px; }
}

/* ---- ≤460px: small phones (375px must be fully usable) ---- */
@media (max-width: 430px) {
  :root { --space-sec: 44px; }
  .wrap { padding: 0 16px; }
  .hero { padding: 44px 0 38px; }
  .hero--compact { padding: 34px 0 28px; }
  /* the 38px crumbs pin would otherwise make compact heroes GROW here */
  .crumbs + .hero--compact { padding-top: 34px; }
  .hero h1 { font-size: 34px; }
  .hero--compact h1 { font-size: 29px; }
  .article h1 { font-size: var(--fs-5xl); }
  .hero__lead { font-size: var(--fs-xl); margin-top: 18px; }
  .hero__meta { gap: 14px 20px; }
  .hero__meta div b { font-size: 26px; }
  .review-flag { flex-wrap: wrap; }
  .sec__head { margin-bottom: 26px; padding-bottom: 14px; }
  .sec__head h2 { font-size: 27px; }
  .stat { padding: 18px; }
  .stat b { font-size: var(--fs-5xl); }
  .brands { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
  .brandchip { min-height: 72px; font-size: var(--fs-md); padding: 10px 12px; }
  .item { padding: 32px 0; }
  .item__no { font-size: 44px; }
  .item h3 { font-size: var(--fs-4xl); }
  .bigscore { font-size: 54px; }
  /* Mobile sizing for the CLASSIC card only. These predate the refonte and
     were still firing on the new card, bumping its 19px title to 21px and its
     22px price down to 20px. The refonte card is sized once and holds at every
     width, so it wants none of them. */
  body.sm-cards-classic .pcard__head { flex-wrap: wrap; }
  body.sm-cards-classic .pcard__body { padding: 18px; }
  body.sm-cards-classic .pcard__title { font-size: 21px; }
  body.sm-cards-classic .pcard__score { font-size: 26px; }
  body.sm-cards-classic .pcard__price { font-size: var(--fs-3xl); }
  .gallery { gap: 10px; grid-auto-rows: 140px; }
  .figfull image-slot { height: 200px; }
  .firm__marker { width: 34px; height: 34px; font-size: var(--fs-md); }
  .buycard__price { font-size: 26px; }
  .buycard__score .bigscore { font-size: 40px; }
  .trust { padding: 16px 12px; }
  .deal__pct { font-size: var(--fs-3xl); }
  .coupon__pct { font-size: 48px; }
  .coupon__pct span { font-size: 26px; }
  .prose { font-size: var(--fs-xl); }
  .prose h3 { font-size: 21px; }
  .prose blockquote { font-size: var(--fs-3xl); }
  .lead-xl { margin-top: 26px; }
  .foot__bottom { flex-direction: column; gap: 8px; }
  .sm-btn { right: 14px; bottom: 14px; padding: 11px 14px; }
  .sm-panel { right: 14px; }
}

/* =========================================================================
   RESPONSIVE — product review supplement (single-sm_product.php)
   ========================================================================= */
.spectable td { overflow-wrap: anywhere; }
.verdict-box, .review-score, .testcard, .buycard { max-width: 100%; }
@media (max-width: 980px) {
  .aside { gap: 0; }
}
@media (max-width: 760px) {
  .subnav a { padding: 14px 16px; min-height: 44px; display: flex; align-items: center; }
  .hero__meta-item { font-size: var(--fs-base); }
  .review-score__overall .bigscore { font-size: 60px; }
}
@media (max-width: 620px) {
  .hero__media img { height: 230px; box-shadow: var(--shadow-3); }
  .verdict-box__main .btn { width: 100%; }
  .verdict-box { padding: 22px; box-shadow: var(--shadow-3); }
  .verdict-box__text { font-size: var(--fs-lg); }
  .dealnow--inline { box-shadow: var(--shadow-2); }
  .dealnow--inline .dealnow__txt { font-size: var(--fs-base); flex: 1 1 100%; }
  .buycard__score { padding: 14px 16px; }
  .buycard__body { padding: 16px; }
  .buycard__trust-item { font-size: var(--fs-md); }
  .testcard { padding: 20px; }
  .gallery { grid-auto-rows: 145px; gap: 12px; }
  .byline { flex-wrap: wrap; }
  .scorebar .lbl { gap: 12px; }
  .scorebar .lbl span { line-height: 1.2; }
}
@media (max-width: 430px) {
  .hero__media img { height: 200px; }
  .review-score { padding: 18px; }
  .review-score__overall { padding-bottom: 18px; }
  .review-score__overall .bigscore { font-size: 52px; }
  .verdict-box__score .bigscore { font-size: 48px; }
  .spectable th, .spectable td { padding: 11px 12px; font-size: var(--fs-md); }
  .spectable th { width: 42%; }
  .firm__head { margin-bottom: 24px; }
  .firm__ends span { font-size: var(--fs-2xs); letter-spacing: .02em; }
  .suit__row { gap: 10px; }
  .suit__row span { font-size: var(--fs-md); }
  .dot { width: 12px; height: 12px; }
  .dots { gap: 4px; }
  .sidecard { padding: 18px; }
  .dealcode--block .dealcode__pct { font-size: var(--fs-3xl); }
  .sec__head .kicker { font-size: var(--fs-2xs); }
}

/* ---- gallery zoom + lightbox ---- */
.gallery__zoom{display:block;width:100%;height:100%;padding:0;border:0;background:none;cursor:zoom-in}
.gallery__zoom img{display:block;width:100%;height:100%;object-fit:cover}
.sm-lightbox{position:fixed;inset:0;z-index:var(--z-toast);display:none;align-items:center;justify-content:center;background:rgba(20,18,15,.92);padding:24px}
.sm-lightbox.is-open{display:flex}
.sm-lightbox img{max-width:92vw;max-height:88vh;object-fit:contain;border:var(--frame-bold) solid #fff;box-shadow:0 20px 60px rgba(0,0,0,.5)}
.sm-lightbox__close{position:absolute;top:18px;right:22px;width:46px;height:46px;background:var(--hi);border:var(--frame) solid var(--ink);color:var(--ink);font-size:var(--fs-4xl);line-height:1;cursor:pointer;font-weight:800}
.sm-lightbox__nav{position:absolute;top:50%;transform:translateY(-50%);width:50px;height:60px;background:rgba(255,255,255,.92);border:var(--frame) solid var(--ink);color:var(--ink);font-size:26px;font-weight:800;cursor:pointer}
.sm-lightbox__nav--prev{left:18px}.sm-lightbox__nav--next{right:18px}
@media (max-width: 620px){.sm-lightbox__nav{width:40px;height:50px;font-size:var(--fs-3xl)}}

/* ---- suitability fallback ---- */
/* Restyled 2026-08-14: was a 26px --pos square with a --frame border, white
   glyph and 700/900 weights. That reads as an awarded badge, but this block is
   the EMPTY state (no product_suitability rows on the product), so it was
   out-shouting the dotted per-position ratings it stands in for. Now the plain
   &#10003; from the template at normal weight, tick tinted --pos and nothing
   else. Deliberately no box: the box is what made it look earned. */
.suit-allround{display:flex;align-items:center;gap:8px;color:var(--ink);font-size:var(--fs-base)}
.suit-allround__ic{flex:none;color:var(--pos)}

/* ---- verdict: author byline + quote (EEAT) ---- */
/* Quote size stepped down 2026-08-12 (design_handoff_produktseite §6 — the ONE
   thing adopted from the prototype's verdict restyle; structure, colours, the
   serif italic and the score column all stay as they were). */
.verdict-box__quote{margin:0;font-family:var(--serif);font-size:clamp(17px,1.6vw,20px);line-height:1.3;font-style:italic;color:#fff}
.verdict-box__author{display:flex;align-items:center;gap:13px;margin-top:20px}
.verdict-box__av{display:grid;place-items:center;width:46px;height:46px;flex:none;background:var(--hi);color:var(--ink);font-family:var(--serif);font-weight:900;border:var(--frame) solid #fff}
.verdict-box__by b{display:block;color:#fff;font-size:var(--fs-base);font-weight:700}
.verdict-box__by small{display:block;color:var(--on-dark-soft);font-size:var(--fs-sm)}

/* ============================================================
   Mega variant C — a wider, more colorful skin of variant B.
   Rendered as B markup; activated by body class .mega-v-c.
   ============================================================ */
.mega-v-c .mega-wrap > .wrap{max-width:min(1480px,calc(100vw - 32px))}
.mega-v-c .mega{grid-template-columns:1fr 360px}
/* colored category icon chips (the "more colors" of variant C) */
.mega-v-c .mega-cats .mega-col:nth-child(3n+1) .mega-ic{background:var(--hi);border-color:var(--ink);color:var(--ink)}
.mega-v-c .mega-cats .mega-col:nth-child(3n+2) .mega-ic{background:var(--hi-2);border-color:var(--ink);color:#fff}
.mega-v-c .mega-cats .mega-col:nth-child(3n)   .mega-ic{background:var(--hi-3);border-color:var(--ink);color:#fff}
.mega-v-c .mega-col .mega-ic{background:var(--hi);border-color:var(--ink);color:var(--ink)}
.mega-v-c .mega-link i{color:var(--hi-2)}
.mega-v-c .mega-feat{background:linear-gradient(155deg,var(--dark-alt) 0%,var(--line) 60%)}
.mega-v-c .mega-feat__tag{background:var(--hi-2);color:#fff}
@media (max-width: 980px){ .mega-v-c .mega{grid-template-columns:1fr} }
/* No hover background/bar on mega links (any variant). */
.mega-link:hover,.mega-v-c .mega-link:hover{background:transparent;box-shadow:none}

/* ============================================================
   Responsive overflow fixes — product single + homepage logos.
   CSS-grid 1fr tracks default to min-width:auto and won't shrink
   below their content's min-content, forcing .main-col and the
   .brandchip cells wider than the viewport. Let them shrink.
   ============================================================ */
.with-aside > *{min-width:0}
.main-col,.aside{min-width:0}
.main-col{overflow-wrap:break-word}
.proscons,.review-score,.testgrid,.gallery,.scorebars{min-width:0}
.proscons > *,.review-score > *,.testgrid > *,.gallery > *,.scorebars > *{min-width:0}
@media (max-width: 620px){.proscons{grid-template-columns:1fr}}
/* Homepage brand logos must stay inside their chip */
.brands > *{min-width:0}
.brandchip{min-width:0;overflow:hidden}
.brandchip img{max-width:100%;max-height:48px;width:auto;object-fit:contain}
/* specs table: let the scroll wrapper scroll instead of growing; sidebar 2-col table fits */
.tbl-scroll{min-width:0}
.aside .spectable{width:100%;table-layout:fixed}
.aside .spectable td,.aside .spectable th{overflow-wrap:anywhere}
/* topbar promo: wrap on small screens instead of nowrap+ellipsis (full offer visible, no overflow) */
@media (max-width: 620px){.topbar{white-space:normal;overflow:visible;text-overflow:clip;line-height:1.35}}

/* ============================================================
   Product gallery — "feature" layout (Customizer: sm_gallery_layout).
   Big full-width title image, then rows of 2 (1 / 2 / 2 …).
   ============================================================ */
.gallery-feature .gallery{grid-template-columns:1fr 1fr;grid-auto-rows:210px}
.gallery-feature .gallery figure{grid-column:auto;grid-row:span 1}
.gallery-feature .gallery figure:first-child{grid-column:1 / -1;grid-row:span 2}
@media (max-width: 760px){
  .gallery-feature .gallery{grid-template-columns:1fr 1fr;grid-auto-rows:150px}
  .gallery-feature .gallery figure:first-child{grid-column:1 / -1;grid-row:span 2}
}
/* contributor photo inside the byline / verdict avatar circle */
.byline__av,.verdict-box__av{overflow:hidden}
.byline__av img,.verdict-box__av img{width:100%;height:100%;object-fit:cover;display:block}
.authorbox__av{overflow:hidden}
.authorbox__av img{width:100%;height:100%;object-fit:cover;display:block;border-radius:inherit}

/* Technische Daten: 2-col label/value table must FIT (no horizontal scroll) */
.spectable{width:100%;table-layout:fixed}
.spectable th{width:42%}
.spectable td,.spectable th{overflow-wrap:anywhere;white-space:normal}
/* …and it carries no frame either: a spec table never scrolls, so the wrapper
   is pure plumbing here. The comparison/deal tables keep .tbl-scroll's frame. */
.tbl-scroll:has(.spectable){overflow-x:visible}
/* breathing room between the test cards (Schlafposition) and the body text */
.testgrid + .prose{margin-top:34px}
/* override the ≤640px .tbl-scroll .tbl{min-width:640px} for 2-col spec tables so they fit */
.tbl-scroll .spectable{min-width:0;width:100%;table-layout:fixed}
/* comparison hero author byline */
.byline--hero{align-items:center}
.hero--dark .byline--hero b{color:#fff}
.hero--dark .byline--hero small{color:var(--on-dark-soft)}

/* ── Star product recommendation (.starreco) ─────────────────────────────
   Variants: --article (in content), --sidebar (aside card), --banner (full
   width below hero). Accent comes from the Customizer via --star-ac; every
   loud element (flag, border, shadow, CTA) derives from it so one color
   pick restyles the whole block. */
.starreco { --star-ac: var(--hi-2); position: relative; background:
  color-mix(in srgb, var(--star-ac) 7%, var(--card));
  border: var(--frame-bold) solid var(--star-ac); box-shadow: 6px 6px 0 var(--star-ac); }
@supports not (background: color-mix(in srgb, red 7%, white)) {
  .starreco { background: var(--card); }
}
.starreco__flag { position: absolute; top: -14px; left: 14px; background: var(--star-ac);
  color: #fff; font: 700 11px/1 var(--sans); letter-spacing: .08em; text-transform: uppercase;
  padding: 6px 12px; box-shadow: var(--shadow-2); }
.starreco__kicker { margin: 0 0 2px; font: 800 11px/1.3 var(--sans); letter-spacing: .08em;
  text-transform: uppercase; color: var(--star-ac); }
.starreco__title { margin: 0 0 6px; font-family: var(--serif); line-height: 1.15; }
.starreco__title a { color: var(--ink); text-decoration: none; }
.starreco__title a:hover { text-decoration: underline; }
.starreco__text { margin: 0 0 12px; color: var(--soft); font-size: var(--fs-base); line-height: 1.5; }
.starreco__row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.starreco__score { font-family: var(--serif); font-weight: 800; font-size: 26px; color: var(--ink); }
.starreco__score small { font-size: var(--fs-sm); color: var(--mute); font-family: var(--sans); font-weight: 600; }
.starreco__cta { background: var(--star-ac); border-color: var(--star-ac); color: #fff; }
/* Hover must set its own colourway: with only a filter here, the base
   .btn:hover still swapped the background to the global --btn-hover-bg — the
   Customizer's button hover (blue on the current palette) against the card's
   red accent. Derive it from the SAME admin-set accent instead: --star-ac
   darkened ~18% toward ink, computed live so any Customizer accent stays
   coherent. (color-mix is already relied on above for the card tint.) */
.starreco__cta:hover {
  background: color-mix(in srgb, var(--star-ac) 82%, var(--ink));
  border-color: color-mix(in srgb, var(--star-ac) 82%, var(--ink));
  color: #fff;
}
.starreco__media img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Article variant: horizontal media + body */
.starreco--article { display: flex; gap: 0; margin-top: 34px; }
.starreco--article .starreco__media { flex: 0 0 220px; border-right: var(--frame-bold) solid var(--star-ac); }
.starreco--article .starreco__body { padding: 24px 22px 18px; }
.starreco--article .starreco__title { font-size: 21px; }
@media (max-width: 620px) {
  .starreco--article { flex-direction: column; }
  .starreco--article .starreco__media { flex-basis: auto; height: 160px; border-right: none; border-bottom: var(--frame-bold) solid var(--star-ac); }
}

/* Sidebar INTEGRATED variant: blends into the .aside rail — no own lining, no
   shadow; accent lives in the badge, tint, score and CTA. Structured like a
   .sidecard section, and its top rule goes the same way as theirs (the rail has
   no frame of its own any more either). */
.starreco--sidebar { border: 0; box-shadow: none; margin: 0; border-top: var(--frame-light) solid var(--line); }
.starreco--sidebar .starreco__flag { position: static; display: inline-block; margin: 16px 0 0 16px; box-shadow: none; }
.starreco--sidebar .starreco__media { display: block; height: 130px; }
.starreco--sidebar .starreco__media img { padding: 12px 16px 0; box-sizing: border-box; }
.starreco--sidebar .starreco__body { padding: 12px 16px 16px; }
.starreco--sidebar .starreco__title { font-size: var(--fs-xl); }
.starreco--sidebar .starreco__text { font-size: var(--fs-sm); }
.starreco--sidebar .starreco__score { font-size: 21px; }

/* Sidebar STANDALONE variant: its own bordered, shadowed object placed
   BELOW the whole sidebar container (inside .aside-wrap). */
/* The wrapper carries the stickiness so the sidebar and the detached star
   scroll as ONE unit (a sticky .aside inside would slide over the card). */
.aside-wrap { position: sticky; top: calc(var(--nav-h) + var(--subnav-h) + 20px); align-self: start; display: flex; flex-direction: column; gap: 34px; min-width: 0; }
.aside-wrap > .aside { position: static; top: auto; }
/* HEIGHT GUARD — a sticky rail taller than the viewport pins its overflow
   permanently off-screen: nothing scrolls it back, so on a 768px laptop a
   product with a coupon plus a star recommendation lost its last card and the
   detached star card entirely. Cap the rail at the room the chrome leaves it
   and let the surplus scroll inside, so no field combination can hide sidebar
   content again. Below 981px the sidebar is a normal in-flow block, hence the
   scoping. The padding/margin pair is net-zero for layout and keeps the
   detached star card's 5px offset shadow outside the clip. */
@media (min-width: 981px) {
  .aside-wrap {
    max-height: calc(100vh - var(--nav-h) - var(--subnav-h) - 40px);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0 8px 8px 0;
    margin: 0 -8px -8px 0;
    scrollbar-width: thin;
    scrollbar-color: var(--ink) var(--hair);
  }
  .aside-wrap::-webkit-scrollbar { width: 8px; }
  .aside-wrap::-webkit-scrollbar-track { background: var(--hair); }
  .aside-wrap::-webkit-scrollbar-thumb { background: var(--ink); border: var(--frame) solid var(--hair); }
}
/* One column: the wrapper carries the stickiness now, so it needs the same
   unsticking .aside gets above. Has to live AFTER the base rule, not in the
   980px block further up — same specificity, so source order decides. */
@media (max-width: 980px) {
  .aside-wrap { position: static; }
}
.starreco--sidebar-detached .starreco__media { display: block; height: 150px; border-bottom: var(--frame-bold) solid var(--star-ac); }
.starreco--sidebar-detached .starreco__body { padding: 16px 16px 14px; }
.starreco--sidebar-detached .starreco__title { font-size: var(--fs-xl); }
.starreco--sidebar-detached .starreco__text { font-size: var(--fs-sm); }
.starreco--sidebar-detached .starreco__score { font-size: 21px; }

/* Banner variant: full content width below hero */
.sec--starbanner { padding: 34px 0 0; }
.starreco--banner { display: grid; grid-template-columns: minmax(240px, 420px) 1fr; }
.starreco--banner .starreco__media { border-right: var(--frame-bold) solid var(--star-ac); min-height: 190px; }
.starreco--banner .starreco__body { padding: 28px 30px 22px; align-self: center; }
.starreco--banner .starreco__title { font-size: clamp(20px, 2.6vw, 28px); }
.starreco--banner .starreco__text { max-width: 62ch; }
@media (max-width: 760px) {
  .starreco--banner { grid-template-columns: 1fr; }
  .starreco--banner .starreco__media { border-right: none; border-bottom: var(--frame-bold) solid var(--star-ac); max-height: 200px; overflow: hidden; }
}

/* Optional coupon inside the star block */
.starreco__deal { margin-top: 14px; }
.starreco--sidebar .starreco__deal, .starreco--sidebar-detached .starreco__deal { margin-top: 12px; }
/* In a rail, drop the masked code box and let the button stand alone.
   .dealcode--block is the only variant that stacks the code box on its own
   line ABOVE a full-width button, so its dotted edge lands right on the
   button's top edge and reads as a decoration belonging to the button (Tim,
   2026-08-23: "dots on the top of the button on sidebar"). Every other
   placement already avoids that: --slim hides the box (line ~927), --cell puts
   it beside the button, --peek lays the opaque button over it. This is the
   same call the comment above --slim records for the sticky product rail — the
   masked bullets are aria-hidden decoration and the button already says a code
   is hidden — it was just never applied to the star block. Scoped to the two
   sidebar variants: in the banner and the in-body star block the stacked box
   has the room and is wanted. */
.starreco--sidebar .starreco__deal:not(.revealed) .dealcode__code,
.starreco--sidebar-detached .starreco__deal:not(.revealed) .dealcode__code { display: none; }

/* Brand logo chip (hero flag row + buycard) — white chip normalizes any
   logo (incl. white-on-transparent ones that vanish on dark/colored bg). */
.hero__flagrow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* .stamp carries a 16px bottom margin for stacked use; in this centred flex row
   it offsets the chip off the row's optical centre. The design handoff zeroes it
   inline (produkt.html) — do it once here instead. */
.hero__flagrow .stamp { margin-bottom: 0; }
/* product_best_for award label. Outlined, not solid: the row already carries a
   solid yellow .review-flag and a solid ink .stamp--brand, and a third filled
   chip flattens the hierarchy. */
.stamp--award { background: transparent; color: var(--hi); border: var(--frame) solid var(--hi); }
a.stamp--brand { text-decoration: none; }
a.stamp--brand:hover { background: var(--hi); color: var(--ink); }
/* Buycard brand logo — bare image, no chip frame */
.buycard__brandlogo { display: inline-flex; margin-bottom: 10px; text-decoration: none; }
.buycard__brandlogo img { display: block; height: 30px; width: auto; max-width: 150px; object-fit: contain; }

/* ============ Comparison layout options (Customizer) ============ */
/* rank numbers off: the 140px number column folds away and the photo grows
   into the freed width instead of leaving a hole beside it. */
.item--nonum{grid-template-columns:1fr}
/* Main photo: classic photo rectangle (4:3), same box as the mini gallery, so
   every ranked item leads with the same shape regardless of what the merchant
   shipped (mostly squares). The FILE is still a soft WP size at its own
   proportions — the crop happens at display time via object-fit, like the
   gallery thumbs. width:100% (not just max-width) because aspect-ratio needs a
   definite width to derive the height from; the actual cap + alignment still
   come from PHP as an inline max-width style (Customizer > Comparison layout >
   product image max width / alignment), shared with .cmp-gal so photo and
   gallery stay one block. */
.item__photo{width:100%;max-width:100%;aspect-ratio:4/3;object-fit:cover;object-position:center;background:var(--card)}
/* Outliers (>=1.9:1 panoramas, <=0.85 portraits) are fitted instead of cropped,
   and DROP the 4:3 box: keeping it letterboxed a 3.05:1 photo into 232px of
   blank paper, half of it sitting between the photo and the mini gallery. That
   was the whole ranked catalogue on a market whose product shots are 1200x393
   (FR, 2026-09-10). Natural ratio instead, capped so a tall portrait cannot run
   away; the gallery below keeps its uniform 4:3 cells because a GRID row would
   go ragged, which is a different problem. Thresholds match .cmp-gal__fit; the
   class is set in single-sm_comparison.php from the attachment's real size. */
.item__photo--fit{object-fit:contain;background:var(--paper);aspect-ratio:auto;max-height:420px}
/* brand logo replaces the brand name (Customizer > Comparison layout) */
.brand--logo img{display:block;height:32px;width:auto;max-width:170px;object-fit:contain}
/* mini gallery under ranked-item photos (Customizer > Comparison layout) */
.cmp-gal{display:grid;gap:10px;margin:-8px 0 22px;align-items:start}
/* Same call as the review gallery's tiles: thumbs carry no frame. (The
   .item--noframe Customizer toggle below is now a no-op for these, kept because
   it also clears the main photo's frame.) */
/* Thumbs share ONE aspect ratio so the row never goes ragged: the source files
   keep their own proportions (soft WP sizes, no regen) and the crop happens at
   display time via object-fit. The main ranked photo is deliberately NOT cropped
   — only this grid is. Switch cover->contain if a product ever gets cut badly;
   that keeps the uniform box and letterboxes instead. */
.cmp-gal img,.cmp-gal video{display:block;width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;object-position:center;border:var(--frame-light) solid var(--line);background:var(--card)}
/* The spotlight tile spans both columns, so a 4:3 box would tower over the two
   thumbs beneath it — give it a wider ratio. */
.cmp-gal--spotlight > :first-child img,.cmp-gal--spotlight > :first-child video{aspect-ratio:16/9}
/* Outliers (>=1.9:1 panoramas, <=0.85 portraits) keep the same box but are fitted
   instead of cropped — at 4:3 they would lose 44-62% of the product. The class is
   set in single-sm_comparison.php from the attachment's real dimensions. */
.cmp-gal img.cmp-gal__fit{object-fit:contain;background:var(--paper)}
/* "framed product images" OFF (Customizer) strips the thumbnail borders too,
   so the main photo and its gallery always read as one treatment. */
.item--noframe .cmp-gal img,.item--noframe .cmp-gal video{border:0}
.cmp-gal--duo{grid-template-columns:1fr 1fr}
.cmp-gal--trio{grid-template-columns:repeat(3,1fr)}
.cmp-gal--quad{grid-template-columns:1fr 1fr}
.cmp-gal--spotlight{grid-template-columns:1fr 1fr}
.cmp-gal--spotlight :first-child{grid-column:1 / -1}

/* ============ Homepage brand strip options (Customizer > Homepage) ============ */
/* auto slider: two identical tracks marquee left in a slow endless loop */
.brands--slider{display:flex;gap:14px;overflow:hidden;flex-wrap:nowrap}
.brands--slider .brands__track{display:flex;gap:14px;flex:none;padding-right:14px;animation:sm-brands-marquee 75s linear infinite}
.brands--slider:hover .brands__track{animation-play-state:paused}
.brands--slider .brandchip{flex:none;width:158px}
@keyframes sm-brands-marquee{from{transform:translateX(0)}to{transform:translateX(-100%)}}
@media (prefers-reduced-motion: reduce){.brands--slider .brands__track{animation:none}}
/* black & white logos — color returns on hover */
.brands--bw .brandchip img{filter:grayscale(1);opacity:.85;transition:filter .25s,opacity .25s}
.brands--bw .brandchip:hover img{filter:none;opacity:1}
/* "Mehr Produktdaten" dropdown in ranked items */
.item__more{margin-top:20px;border:var(--frame) solid var(--line);background:var(--card)}
/* This is a BOX, not a separator: all four sides on the 2px frame, from the
   shorthand above alone. It briefly needed a `:first-of-type` restatement here
   because the FAQ block styled bare `details` and out-specified that shorthand
   on the top edge; the FAQ rules are scoped to .sec--faq now, so the cause is
   gone and the patch with it. */
.item__more summary{cursor:pointer;font-family:var(--sans);font-size:var(--fs-xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;padding:12px 16px;list-style:none;display:flex;justify-content:space-between;align-items:center;gap:12px}
.item__more summary::-webkit-details-marker{display:none}
.item__more summary::after{content:"+";font-size:var(--fs-xl);font-weight:800;color:var(--hi-2)}
.item__more[open] summary{border-bottom:var(--frame) solid var(--line)}
.item__more[open] summary::after{content:"\2013"}
.item__specs{margin:0;padding:6px 16px 12px}
.item__specs > div{display:flex;gap:16px;padding:9px 0;border-bottom:var(--frame-hair) solid var(--hair);font-size:var(--fs-md)}
.item__specs > div:last-child{border-bottom:0}
.item__specs dt{flex:0 0 110px;font-family:var(--sans);font-weight:800;font-size:var(--fs-2xs);letter-spacing:.08em;text-transform:uppercase;color:var(--ink);padding-top:2px}
.item__specs dd{margin:0;color:var(--soft)}

/* ============ Kaufberater strip on produktart archives ============ */
.tax-guides{display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px;margin-bottom:26px;padding:16px 18px;border:var(--frame) solid var(--line);background:var(--card)}
.tax-guides__lbl{font-family:var(--sans);font-size:var(--fs-2xs);font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);border-bottom:var(--frame) solid var(--hi);padding-bottom:2px;margin-right:6px}

/* ============ Nav/footer labels demoted from headings ============ */
/* (were h4/h5/h6 — heading-order noise in every page's outline; classes
   mirror the exact styles the tag selectors applied) */
.mega-h{font-family:var(--sans);font-weight:800;font-size:var(--fs-xs);letter-spacing:.12em;text-transform:uppercase;color:var(--ink);margin:0 0 8px;padding:0 0 12px;border-bottom:var(--frame-light) solid var(--line);display:flex;align-items:center;gap:10px}
.mega-h.mega-col__label{display:block}
/* The category panel's header row IS the parent comparison — a link, not a
   label. It reuses .mega-h wholesale (chip, type, underline, flex row), so the
   classic skin keeps its uppercase sans and .nav--st keeps its Playfair; only
   the count, the arrow and the hover are new. */
/* The parent-comparison heading is a link like the rows beneath it, so it hovers
   like them: the yellow underline under the name, not a colour swap. It carries
   no arrow — the .mega-link arrows exist to fade in on hover as the affordance,
   and a permanently visible one on the heading just read as noise. */
.mega-h--parent .mega-h__name{transition:box-shadow .12s}
.mega-h--parent:hover .mega-h__name{box-shadow:inset 0 -2px var(--hi)}

/* ── Mega-panel horizontal dividers, made visible ───────────────────────────
   The stock rules draw NOTHING: .mega-h and .mega-link--all use
   var(--frame-light), which is 0 in the token set, and .mega-link uses
   var(--hair), which production overrides to #ffffff. So the existing
   sm_mega_div_h toggle looked dead — there was never a line to turn off.
   Explicit weight and colour here, because both tokens are unusable.
   body:not(.sm-mega-nodivh) keeps that toggle authoritative rather than
   relying on source order against its rules further down. */
body:not(.sm-mega-nodivh) .mega-h,
body:not(.sm-mega-nodivh) .nav--st .mega-h{border-bottom:1px solid rgba(0,0,0,.10)}
body:not(.sm-mega-nodivh) .mega-link--all,
body:not(.sm-mega-nodivh) .mega-foot,
/* Same story: .mega-cards draws its separator with --frame-light (0). */
body:not(.sm-mega-nodivh) .mega-cards{border-top:1px solid rgba(0,0,0,.10)}

/* ── Light bordering (Customizer → Navigation) ──────────────────────────────
   Trades the hard Stark frames for elevation. The heavy frames are declared in
   the .nav--st block ABOVE at equal specificity, so every selector here pairs
   .nav--light WITH .nav--st to win on specificity rather than source order. */

/* Sticky header: a lift instead of a rule. */
.nav--light{border-bottom:0;box-shadow:0 6px 18px rgba(17,35,47,.07)}
.nav--st.nav--light .nav-st__row1{border-bottom:1px solid rgba(0,0,0,.08)}

/* Mega panel: the 3px ink rule and the 2px column dividers come off, and the
   panel is separated by the drop shadow instead. Replaces the stock
   0 30px 50px rgba(0,0,0,.18), which is far heavier than a light treatment. */
.nav--light .mega-wrap,
.nav--st.nav--light .mega-wrap{border-top:0;border-bottom:0;box-shadow:0 18px 36px rgba(17,35,47,.10)}
.nav--light .mega{border:0}
.nav--st.nav--light .mega > .mega-col,
.nav--st.nav--light .mega-cats,
.nav--st.nav--light .mega-cats .mega-col{border-right:1px solid rgba(0,0,0,.07)}
.nav--st.nav--light .mega-cats .mega-col:last-child{border-right:0}
.nav--st.nav--light .mega-feat{border-color:rgba(0,0,0,.07)}

/* Everything else framed inside the header. These all carry a 2px --ink frame
   at full strength, which is what still reads as "hard" once the panel itself
   has gone soft. One local token so the weight can be tuned in one place. */
/* --nav-line now lives on :root (see the token block) — kept here as the local
   tuning point: change it on .nav--light to move the header's frames alone. */
.nav--light{--nav-line:rgba(0,0,0,.14)}
.nav--light .nav-search,
.nav--light .nav-search--st,
/* .nav--st re-declares the search border-color further down at equal
   specificity, so the stacked variant has to be matched explicitly. */
.nav--st.nav--light .nav-search--st{border:1px solid var(--nav-line)}
.nav--light .nav-search__btn{border-left:1px solid var(--nav-line)}
.nav--light .nav-cta{border:1px solid var(--nav-line)}
/* The Testberichte / Vergleiche / Rabatte cards carry the same 2px frame. */
.nav--light .mega-card,
.nav--light .mega-band{border:1px solid var(--nav-line)}
/* Category picto beside a panel heading. NOT .mega-card__ic — a frame around
   the icon inside a card reads as a box in a box. */
.nav--light .mega-ic{border:1px solid var(--nav-line)}
/* The tab row's bottom edge is what reads as a heavy rule across the top of an
   open mega panel. */
.nav--st.nav--light .nav-st__row2{border-bottom:1px solid var(--nav-line)}
/* Live/deal banner. It draws with --frame-light, which is 0, so it has no
   frame at all today; light bordering gives it a hairline like its siblings. */
.nav--light .dealnow{border:1px solid var(--nav-line)}

/* (The old `.nav--light .mobile-nav` top/bottom bookends are gone: they belonged
   to the in-flow sheet — on the fixed full-screen modal they drew stray hairlines
   along the screen edges.) */
.nav--light .mobile-nav .mnav-card,
.nav--light .mobile-nav .mnav-cards .mnav-card{border-color:var(--nav-line)}

/* ── Mobile menu: restore the row separators ────────────────────────────────
   NOT gated behind light bordering — this is the same class of bug as the mega
   dividers. .mobile-nav a draws its separator with var(--hair), which the live
   sm-token-overrides block sets to #ffffff, so every row divider is white on
   white and the panel reads as one undifferentiated column with no sense of
   where a section starts or ends. Explicit colour, same reason as above. */
/* Entries INSIDE a dropdown are not separated individually — the group boundary
   carries the structure, and a rule per row made the panel read as a table. */
.mobile-nav .mnav-group__panel a{border-bottom:0}
/* Top-level entries that are NOT dropdowns (Testberichte, and any other bare
   link between the groups) close with the same rule a collapsible group gets,
   so the top level reads as one consistent list, and take the same 52px row
   height as the group buttons. The CTA button is excluded: it carries its own
   full frame. (These anchors live inside .mnav__level--root since the modal
   rebuild — a `.mobile-nav > a` child selector no longer matches anything.) */
.mobile-nav .mnav__level--root > a:not(.mnav-cta){min-height:52px;border-bottom:1px solid rgba(0,0,0,.16)}
/* The featured comparison is introduced by a rule ABOVE it, not closed by one
   below it. */
.mobile-nav .mnav-parent{border-bottom:0;border-top:1px solid rgba(0,0,0,.10)}
/* The first group sat under a 2px --line rule below the search field. */
.mobile-nav .nav-search--full + .mnav-group,
.mobile-nav .nav-search--full + a{border-top:1px solid rgba(0,0,0,.10)}
/* The Weitere entries are borderless rows since 2026-08-20 — the light-skin
   card frames come off with the card look. */
.nav--light .mobile-nav .mnav-card,
.nav--light .mobile-nav .mnav-cards .mnav-card{border:0}
.nav--light .mobile-nav .mnav-cta{border:1px solid var(--nav-line)}
/* Close the Testberichte / Vergleiche / Rabatte block: it had no bottom edge,
   so the cards ran straight into whatever followed. */
.mobile-nav .mnav-cards{border-bottom:1px solid rgba(0,0,0,.10)}
/* Collapsible groups already declare their own separator with --hair, so it is
   invisible for the same reason. Restore it slightly heavier than a row rule so
   a section boundary reads as stronger than the rows inside it. */
.mnav-group{border-bottom:1px solid rgba(0,0,0,.16)}
.mnav-group:last-child{border-bottom:0}
.mega-h__name{min-width:0}
/* Neither uppercased nor tracked, whichever skin the row is rendered in. */
/* Tested-model count. It was small grey text hanging off the baseline of a big
   serif heading, which read as leftover copy rather than data. As a filled chip
   it speaks the same language as .item__award and .stamp, and the count is the
   reason to click, so it should look deliberate. flex:none keeps it whole when
   a long category name squeezes the row. */
.mega-h__n{flex:none;display:inline-block;font-family:var(--sans);font-weight:800;font-size:11px;line-height:1.5;letter-spacing:.1em;text-transform:uppercase;color:var(--ink);background:var(--hi);padding:3px 9px;border-radius:var(--radius-btn);white-space:nowrap}
.mega-t{font-family:var(--serif);font-weight:800;line-height:1.0;letter-spacing:-0.01em;margin:0}
.mega-feat__body .mega-t{color:#fff;font-size:21px;line-height:1.12}
.foot__h{font-family:var(--sans);font-size:var(--fs-2xs);letter-spacing:.14em;text-transform:uppercase;color:var(--on-dark-mute);font-weight:700;margin:0 0 16px}

/* full-bleed slider: true viewport width, breaking out of the .wrap gutter */
.brands--full{width:100vw;margin-left:calc(50% - 50vw)}
/* frameless logos: no grid lines, no chip background */
.brands--frameless{border:0}
.brands--frameless .brandchip{border:0;background:transparent}
.brands--frameless .brandchip:hover{background:transparent}

/* sticky nav opt-out (Customizer > Navigation) */
.nav--static{position:static}

/* ad-notice kill switches (Customizer > Advertisement notice, per placement) */
.sm-no-ad-notice .anz,
.sm-no-ad-notice .buycard__disc,
.sm-no-ad-notice .sm-affnote{display:none!important}
.sm-no-ad-footer .sm-affnote--footer{display:none!important}

/* slider mode reads as a slim band — the full --space-sec padding dwarfs it */
.sec--card:has(.brands--slider){padding:28px 0}

/* =========================================================================
   NAV — STACKED LAYOUT (Customizer > Navigation > LAYOUT)
   Two rows: logo · featured search · deals CTA / category tab bar.
   The mega panels reuse the shared markup, reskinned as a white sheet
   with the featured card (300px) at the right of a vertical divider.
   ========================================================================= */
.nav--st{background:var(--card);border-bottom:0}
/* row 1 stacks ABOVE row 2 (42 > 41) — the search suggestions dropdown lives
   inside row 1 and must overlap the tab bar below it */
.nav--st .nav-st__row1{border-bottom:var(--frame) solid var(--line);position:relative;z-index:calc(var(--z-navrow) + 1);background:var(--card)}
.nav--st .nav-st__top{display:flex;align-items:center;gap:22px;padding-top:14px;padding-bottom:14px}
.nav--st .logo{flex:none}

/* featured search — the row-1 centerpiece */
/* Field surface one step lighter than --hair-soft (same --line/--card recipe
   the token itself uses, at half the tint), and the box rounds to the SAME
   literal 10px as the Suchen button inside it so the pair reads as one piece. */
.nav--st .nav-search--st{flex:1;min-width:0;height:48px;border-color:var(--line);background:color-mix(in srgb, var(--line) 8%, var(--card));border-radius:10px;align-items:center;gap:10px;padding:0 8px 0 14px}
.nav--st .nav-search--st .nav-search__ic{width:18px;height:18px;color:var(--mute);flex:none}
.nav--st .nav-search--st .nav-search__input{flex:1;width:auto;font-size:var(--fs-base);padding:0}
/* …and the "Suchen" submit rounds slightly (§2, the only other search change).
   Literal 10px: --radius-btn is the site-wide CTA radius (20px in production)
   and this button is deliberately tighter than that. */
.nav--st .nav-search__btn--txt{width:auto;height:34px;padding:0 16px;border-left:0;border-radius:10px;background:var(--ink);color:#fff;font-family:var(--sans);font-size:var(--fs-md);font-weight:700}
/* Hover = the sitewide button hover (--hi + ink, the skin's light blue — same
   pairing as .btn:hover and the mega-panel underlines). Was --hi-2, the one
   orange hover in an otherwise blue navbar. */
.nav--st .nav-search__btn--txt:hover{background:var(--hi);color:var(--ink)}

/* deals CTA — live pulse dot */
.nav--st .nav-cta{display:inline-flex;align-items:center;gap:9px}
.nav-cta__dot,.mega-band__dot{position:relative;width:9px;height:9px;border-radius:50%;background:var(--neg);flex:none}
.nav-cta__dot::after,.mega-band__dot::after{content:"";position:absolute;inset:0;border-radius:50%;background:var(--neg);animation:sm-livepulse 1.8s ease-out infinite}
@media (prefers-reduced-motion:reduce){.nav-cta__dot::after,.mega-band__dot::after{animation:none}}

/* row 2 — category tab bar */
.nav--st .nav-st__row2{border-bottom:var(--frame) solid var(--line);background:var(--card);position:relative;z-index:var(--z-navrow)}
.nav--st .nav__menu{gap:0}
.nav--st .nav__menu > a,.nav--st .megabtn{padding:15px 18px;font-size:var(--fs-base);font-weight:600}
.nav--st .nav__menu > a:hover,.nav--st .megabtn:hover{background:var(--paper)}
.nav--st .has-mega.is-open .megabtn{background:var(--paper)}

/* panels — full-width white sheet, no outer box */
.nav--st .mega-wrap{background:var(--card);border-top:0;border-bottom:var(--frame-bold) solid var(--line)}
.nav--st .mega-wrap > .wrap{padding-top:28px;padding-bottom:28px}
.nav--st .mega{display:flex;gap:0;border:0;background:none}
.nav--st .mega > .mega-col,.nav--st .mega-cats{flex:1;min-width:0;border-right:var(--frame) solid var(--line);padding:0 30px 0 0}
.nav--st .mega-cats{display:grid;grid-template-columns:1fr 1fr}
.nav--st .mega-cats .mega-col{border-right:var(--frame) solid var(--hair);padding:0 24px}
.nav--st .mega-cats .mega-col:first-child{padding-left:0}
.nav--st .mega-cats .mega-col:last-child{border-right:0;padding-right:0}
.nav--st .mega-feat{flex:none;width:300px;margin-left:30px;border:var(--frame-light) solid var(--line);align-self:flex-start}
.nav--st .mega-feat__img{height:150px;border-bottom:var(--frame-light) solid var(--line)}
/* The heading rule ABOVE the link list, stacked-nav version — this is the one
   that actually renders on the site (.nav--st), so softening .mega-h alone
   would have changed nothing. */
.nav--st .mega-h{font-family:var(--serif);font-weight:700;font-size:var(--fs-3xl);letter-spacing:0;text-transform:none;gap:10px;padding-bottom:14px;margin-bottom:10px;border-bottom:var(--frame-light) solid var(--line)}
.nav--st .mega-ic{width:32px;height:32px;background:var(--hi);border-color:var(--line)}
.nav--st .mega-ic svg{width:18px;height:18px}
.nav--st .mega-link{border-bottom:0;padding:8px;margin:0 -8px;font-weight:500}
.nav--st .mega-link:hover{background:transparent;box-shadow:none}
.nav--st .mega-catdeal{border-bottom:0;padding:9px 8px}
.nav--st .mega-kicker{margin-bottom:10px}
/* panel-link hover = the Ratgeber pattern: quiet light-blue underline under
   the label (no background or text-color swap). Applies to BOTH layouts. */
.mega-link:hover > span{box-shadow:inset 0 -2px var(--hi)}
.nav--st .mega-link--all{border-top:var(--frame-light) solid var(--line);padding-top:13px;font-weight:800}
.nav--st .mega-art{border-bottom:var(--frame-hair) solid var(--hair)}

/* category panel footer: reviews/comparisons cards + deals band.
   The rule BELOW the list goes; .mega-card's own frame is what the request
   keeps ("around Testberichte & Vergleiche"). */
.mega-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(0,1fr));gap:14px;margin-top:20px;padding-top:18px;border-top:var(--frame-light) solid var(--line)}
.mega-card{display:flex;align-items:center;gap:11px;border:var(--frame) solid var(--line);padding:13px 15px;color:var(--ink);background:var(--card);min-width:0}
.mega-card:hover{background:var(--hi)}
.mega-card__ic{flex:none;display:grid;place-items:center;color:var(--ink)}
.mega-card__ic svg{width:20px;height:20px;display:block}
.mega-card__t{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.mega-card__t b{font-size:var(--fs-base);font-weight:700}
.mega-card__t small{font-size:var(--fs-sm);color:var(--mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mega-card > i{margin-left:auto;font-style:normal;font-weight:800;color:var(--hi-2)}
.mega-band{display:flex;align-items:center;flex-wrap:wrap;gap:6px 13px;margin-top:16px;border:var(--frame) solid var(--line);background:var(--hi);padding:13px 16px;color:var(--ink);box-shadow:var(--shadow-line);transition:background .12s,color .12s}
.mega-band:hover{background:var(--ink);color:#fff}
.mega-band b{font-size:var(--fs-base);font-weight:700}
.mega-band small{font-size:var(--fs-sm);opacity:.8}
.mega-band > i{margin-left:auto;font-style:normal;font-weight:800}

/* scrim behind the open panel (stacked only renders .megabg) */
.nav--st .megabg{background:rgba(17,23,29,.34)}

@media (max-width: 980px){
  /* logo + burger only — the search lives inside the burger panel on mobile */
  .nav--st .nav-st__top{flex-wrap:wrap;gap:12px;padding-top:12px;padding-bottom:12px}
  .nav--st .nav-burger{margin-left:auto}
  .nav--st .nav-search--st{display:none}
  .nav--st .nav-cta{display:none}
  .nav--st .nav-st__row2{display:none}
  .nav--st .megabg{display:none!important}
  /* ≥16px stops iOS from auto-zooming the page on focus */
  .nav-search__input{font-size:var(--fs-lg)}
}

/* =========================================================================
   MEGA MENU — chrome kill switches (Customizer > Navigation, both layouts)
   ========================================================================= */
.sm-mega-noicon .mega-ic{display:none!important}
.sm-mega-icnoborder .mega-ic{border:0!important}
.sm-mega-icnobg .mega-ic{background:transparent!important;color:var(--ink)!important}
/* vertical divider before the featured card */
.mega-v-c.sm-mega-nodivv .mega-feat{border-left:0}
.sm-mega-nodivv .nav--st .mega > .mega-col,
.sm-mega-nodivv .nav--st .mega-cats{border-right:0}
.sm-mega-nodivv .mega-split__col{border-right:0}
/* horizontal rules inside the panels */
.sm-mega-nodivh .mega-h{border-bottom:0!important}
.sm-mega-nodivh .mega-link{border-bottom:0}
.sm-mega-nodivh .mega-catdeal{border-bottom:0}
.sm-mega-nodivh .mega-link--all,.sm-mega-nodivh .mega-foot{border-top:0}
.sm-mega-nodivh .mega-cards{border-top:0}
.sm-mega-nodivh .mega-art{border-bottom:0!important}
/* comparison links split into two columns (category panels) */
.sm-mega-cmp2col .mega-links{columns:2;column-gap:24px}
.sm-mega-cmp2col .mega-links .mega-link{break-inside:avoid}
@media (max-width: 980px){.sm-mega-cmp2col .mega-links{columns:1}}
/* …unless the panel already renders the LABELLED split ("Nach Bedarf" /
   "Nach Material"). That split IS the two columns; leaving the multicol on
   fragments each of them again into two — four columns, kickers stranded
   over the wrong lists. Four links per labelled column, not two per flow. */
.sm-mega-cmp2col .mega-links:has(.mega-split--cat){columns:auto}

/* =========================================================================
   NAV SEARCH — live suggestions dropdown (all nav search boxes)
   ========================================================================= */
.nav-search{position:relative}
.nav-sugg{position:absolute;top:calc(100% + 4px);right:-2px;left:auto;width:min(400px,calc(100vw - 24px));z-index:var(--z-suggest);background:var(--card);border:var(--frame) solid var(--line);box-shadow:6px 6px 0 rgba(0,0,0,.14);max-height:min(480px,70vh);overflow:auto;text-align:left}
.nav-search--st .nav-sugg{left:-2px;right:-2px;width:auto}
.nav-sugg__lbl{font-size:var(--fs-2xs);font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--hi-2);padding:12px 14px 5px}
/* two-class selector: must outrank the generic `.mobile-nav a` block styling
   (uppercase, 16px padding, borders) when the dropdown opens inside the
   burger panel — otherwise the rows render mangled there */
.nav-sugg .nav-sugg__item{display:flex;align-items:center;gap:12px;padding:9px 14px;color:var(--ink);font-weight:600;font-size:var(--fs-md);letter-spacing:0;text-transform:none;border-bottom:0}
.nav-sugg .nav-sugg__item:hover,.nav-sugg .nav-sugg__item.is-active{background:var(--hi)}
.nav-sugg__img{flex:none;width:46px;height:34px;border:var(--frame) solid var(--line);background:var(--paper);overflow:hidden}
.nav-sugg__img img{width:100%;height:100%;object-fit:cover;display:block}
.nav-sugg__img--logo{background:var(--card)}
.nav-sugg__img--logo img{object-fit:contain;padding:3px}
.nav-sugg__img--empty{display:none}
.nav-sugg__t{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.nav-sugg__t b{font-size:var(--fs-md);font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nav-sugg__t small{font-size:var(--fs-xs);color:var(--mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---- search button → spinner while suggestions load (form.is-loading set
   by initSuggest; covers desktop text btn, desktop icon btn, mobile) ---- */
@keyframes sm-search-spin{to{transform:rotate(360deg)}}
.nav-search__btn{position:relative}
.nav-search.is-loading .nav-search__btn{color:transparent}
.nav-search.is-loading .nav-search__btn svg{visibility:hidden}
.nav-search.is-loading .nav-search__btn::after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:14px;height:14px;border:var(--frame) solid var(--ink);border-right-color:transparent;border-radius:50%;animation:sm-search-spin .6s linear infinite}
/* dark / hover backgrounds need a white spinner */
.nav-search.is-loading .nav-search__btn--txt::after,
.nav-search.is-loading .nav-search__btn:hover::after{border-color:#fff;border-right-color:transparent}
/* …except the Suchen text button's hover, which is now the LIGHT --hi surface
   (see .nav--st .nav-search__btn--txt:hover) — a white spinner vanishes there. */
.nav-search.is-loading .nav-search__btn--txt:hover::after{border-color:var(--ink);border-right-color:transparent}

/* =========================================================================
   PAGINATION — the site's single pager appearance
   Covers BOTH pager sources so they render identically:
     1. WordPress `the_posts_pagination()` — archive.php, home.php, index.php
        (nav.pagination > .nav-links > a/span.page-numbers)
     2. the client-side catalog pager built by initProductFilter (.sm-pager), which
        reuses the same class names on <button> elements.
   Until now neither had a single rule, so every archive pager rendered as raw
   body text. Tokens only; flat borders, no radii, uppercase micro-labels.
   ========================================================================= */
.pagination{margin:46px 0 6px;padding-top:26px;border-top:var(--frame) solid var(--line)}
.pagination[hidden]{display:none}
.pagination .nav-links{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:8px}
.pagination .nav-links[hidden]{display:none}
/* 46px box = tap target above the 44px floor on every breakpoint */
.pagination .page-numbers{display:inline-flex;align-items:center;justify-content:center;min-width:46px;min-height:46px;padding:0 13px;margin:0;font-family:var(--sans);font-size:var(--fs-md);font-weight:700;line-height:1;font-variant-numeric:tabular-nums;color:var(--ink);background:var(--card);border:var(--frame) solid var(--line);text-decoration:none;cursor:pointer;transition:background .15s,color .15s}
.pagination .page-numbers.prev,
.pagination .page-numbers.next{font-size:var(--fs-xs);font-weight:800;letter-spacing:.14em;text-transform:uppercase;padding:0 16px}
.pagination a.page-numbers:hover,
.pagination button.page-numbers:hover{background:var(--hi);color:var(--ink)}
/* current page — inverted plus an offset accent block, unmistakable at a glance */
.pagination .page-numbers.current{background:var(--ink);color:var(--paper);border-color:var(--ink);box-shadow:var(--shadow-hi-sm);cursor:default}
.pagination .page-numbers.dots{min-width:22px;padding:0 2px;background:none;border-color:transparent;color:var(--mute);cursor:default}
/* The theme defines focus nowhere — this component brings its own ring.
   --hi-2 (not --hi) so it still reads against the yellow hover fill. */
.pagination .page-numbers:focus-visible,
.sm-pager__top:focus-visible{outline:3px solid var(--hi-2);outline-offset:2px}
/* result range under the JS pager ("17 – 32 von 114 Produkten") */
.sm-pager__status{margin-top:14px;text-align:center;font-family:var(--sans);font-size:var(--fs-sm);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--mute);font-variant-numeric:tabular-nums}
.sm-pager__top{display:block;margin:12px auto 0;padding:6px 2px;font-family:var(--sans);font-size:var(--fs-xs);font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--ink);background:none;border:0;border-bottom:var(--frame) solid var(--hi-2);cursor:pointer}
.sm-pager__top:hover{background:var(--hi)}
/* programmatic focus target: paging returns focus + viewport to the grid top */
.grid-3[tabindex]:focus{outline:none}
@media (max-width: 620px){
  .pagination{margin-top:34px}
  .pagination .nav-links{gap:6px}
  /* keep the 44px floor; only the horizontal padding gives way */
  .pagination .page-numbers{min-width:44px;min-height:44px;padding:0 8px}
  .pagination .page-numbers.prev,
  .pagination .page-numbers.next{padding:0 12px}
}

/* ═══════════════════════════════════════════════════════════════════════════
   COUPON REVEAL UX — appended block (deal rows, masked reveal, duplicate
   collapse). Self-contained: it adds new selectors and two modifiers only,
   nothing above is restructured or overridden by rewriting.
   ═══════════════════════════════════════════════════════════════════════════ */

/* caption under the value figure inside the reveal widget (pct / pct_sub args) */
.dealcode__pct small{display:block;margin-top:3px;font-family:var(--sans);font-size:var(--fs-3xs);font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--mute)}

/* Word labels ("Gutschein" / "Aktion") in the value box: the serif display
   size is cut for "40 %" and overflows the box on a whole word. */
.deal__pct--txt{font-size:19px;line-height:1.05;letter-spacing:-.01em}

/* "N weitere Codes für dasselbe Angebot": near-duplicate offers stay on the
   page, one click away, instead of flooding the list. */
.dealmore{border-bottom:var(--frame-hair) solid var(--hair)}
/* Neither variant declares a top border, and .dealmore--card sets border-top:0
   explicitly because it is tucked under the card above it by a negative margin.
   That used to need defending against the FAQ block's `details:first-of-type`;
   with those rules scoped to .sec--faq it no longer does. */
.dealmore[open]{background:var(--paper)}
.dealmore__sum{display:flex;align-items:center;gap:9px;list-style:none;cursor:pointer;padding:10px 22px;font-family:var(--sans);font-size:var(--fs-xs);font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
.dealmore__sum::-webkit-details-marker{display:none}
.dealmore__sum::before{content:"+";font-size:var(--fs-base);line-height:1;font-weight:800;color:var(--ink)}
.dealmore[open] .dealmore__sum::before{content:"–"}
.dealmore__sum:hover{color:var(--ink);background:var(--hi)}
.dealmore__sum:focus-visible{outline:2px solid var(--ink);outline-offset:-2px}
.dealmore__body{border-top:var(--frame-hair) solid var(--hair)}

/* Card context (brand page): the disclosure clips onto the card above it. */
.dealmore--card{border:var(--frame) solid var(--line);border-top:0;background:var(--card);margin:-18px 0 18px}
.dealmore--card .dealmore__sum{padding:11px 22px}
.dealmore--card .dealmore__body{padding:14px 16px 0}
.dealmore--card .deal{margin-bottom:14px}

@media (max-width: 620px){
  .dealmore__sum{padding:10px 16px}
  .dealmore--card{margin:-14px 0 14px}
  .dealmore--card .dealmore__body{padding:12px 10px 0}
}

/* =========================================================================
   UTILITIES & components promoted out of template inline styles (2026-08).
   Everything here replaces markup that could not be restyled from CSS at
   all. Reach for these before writing another style="" in a template.
   ========================================================================= */

/* Card surface — the recipe .pc/.buycard/.deal/.gallery figure all
   share. Use it on new components instead of restating border+background. */
.surface{border:var(--frame) solid var(--line);background:var(--card)}
.surface--bold{border:var(--frame-bold) solid var(--ink);background:var(--card)}

/* .muted was used in single-sm_product.php but had never been defined, and
   ~25 templates hand-wrote style="color:var(--mute)" to get the same thing. */
.muted{color:var(--mute)}

/* Empty state — was repeated inline across six templates. */
.sm-empty{padding:var(--sp-9) 0;text-align:center;color:var(--mute)}
.sm-empty--tight{padding:var(--sp-7) 0}
.sm-empty--flush{padding:var(--sp-6) 0 var(--sp-8)}
.sm-empty__lead{color:var(--soft);margin-top:var(--sp-3)}
.sm-empty .btn{margin-top:var(--sp-5)}

/* Compact data table — the comparison overview and deal tables set
   font-size:13px inline on every single cell to get this. */
.tbl--compact td,.tbl--compact td strong,.tbl--compact td span{font-size:var(--fs-sm)}
.tbl--compact td.is-nowrap{white-space:nowrap}
.tbl--compact td strong.is-block{display:block}
/* Columns that drop out of a compact table before it starts scrolling.
   ---------------------------------------------------------------------------
   The 10-column comparison overview cannot be squeezed: its min-content width
   is 1078px against the 1096px .wrap gives it, i.e. 18px of slack. So every
   viewport below 1160px — where .wrap stops being capped and starts tracking
   the window — put a scrollbar under the table for a few dozen px: 42px at
   1100, 102px at 1040. What sets that 1078px floor is unbreakable text, not
   the layout: German compounds ("Tonnentaschenfederkernmatratze" is 253px on
   its own) and one-word headers like "Probeliegen".

   Breaking words was the obvious fix and it is the wrong one. overflow-wrap:
   anywhere does lower the floor (1078 -> 830) but it renders "Mittelf/est" and
   "Druckentlastun/g", because hyphens:auto cannot save it: Chrome's
   hyphenation dictionary is a downloadable component, absent often enough that
   a probe here found NO hyphenation at all (identical wrap height under
   hyphens:auto and hyphens:manual). It also measurably does nothing for the
   floor on its own, so it buys the mangling for free.

   Dropping the Material cell instead costs nothing that isn't already gone:
   the template trims it to 14 words with an ellipsis precisely because the
   full spec paragraph is unreadable in a column, and the complete text is on
   the review each row links to. Floor 1078 -> 911, which fits every width down
   to ~975px, and the rows halve (156px -> 93px) because that one cell was what
   made them tall. Below ~975 .tbl-scroll scrolls as designed — but with one
   column less to drag through.

   That 18px of slack is gone (2026-08-14). The floor is now 1140px against the
   same 1096px, so the table scrolled 44px at EVERY width from 1160 up — and the
   44px hidden past the edge was the Angebot CTA column, the one cell on the row
   that has to be clickable. Material's own floor is 172px of truncated prose,
   so it is what grew; nothing about the layout changed.

   The `max-width:1159px` bound was therefore inverted: .wrap caps at 1160 and
   pads 32 either side, so this table is handed 1096px at 1160px and at 2560px
   alike. There is no viewport where the Material column fits, which means the
   media query showed it only where it could not fit and hid it everywhere else.
   Unconditional now — every argument above for dropping the cell applies at all
   widths, and the rows halve at desktop too. Re-check the floor against 1096 if
   a column is ever added back. */
.tbl--compact .col-optional{display:none}

/* …and the same idea as a ladder for the widths BELOW the .wrap cap, where the
   remaining 9 columns still do not fit: 967px of column floors against 960px at
   1024, 836 at 900, 704 at 768. Every column is already at its min-content, so
   there is nothing to redistribute — the only lever is column count.
   Shed order is least-decisive first, measured floors in brackets:
     -1 Garantie      [89]  <=1100  — a warranty year rarely picks the mattress
     -2 Probeliegen  [112]  <=1000  — trial nights, same
     -3 Härte         [82]   <=880  — on the card and the review it links to
     -4 Empfohlen für[116]   <=880  — the longest of the four
   What survives to the narrow end is rank, product, rating, price and the CTA,
   i.e. everything the row is actually for.
   The <=880 step also has to lift `.tbl-scroll .tbl{min-width:760px}` (line 595)
   for THIS table: with four columns gone the floor is 568px, and a 760px minimum
   is what would keep it scrolling. Scoped to .tbl--compact and only inside this
   query, so the floor still protects every other .tbl-scroll table and still
   protects this one at the widths where all nine columns are shown.
   Verified 1440 → 700px scroll-free. Below ~620px it scrolls again: four columns
   need 568px against 580px at 620 and 398px at 430, so a phone cannot hold this
   table at all. Dropping Bewertung would buy ~100px and one more breakpoint;
   that is an editorial call about what the table is for, not a layout fix, and
   the ranked cards above already carry the same data in a phone-shaped form. */
@media (max-width: 1100px){
  .tbl--compact .col-shed-1{display:none}
}
@media (max-width: 1000px){
  .tbl--compact .col-shed-2{display:none}
}
@media (max-width: 880px){
  .tbl--compact .col-shed-3,
  .tbl--compact .col-shed-4{display:none}
  .tbl-scroll .tbl--compact{min-width:0}
}

/* Vertical rhythm — replaces ~30 inline margin-top values (8-26px) that
   were within 2px of each other. Density-aware, unlike the literals. */
.stack-1{margin-top:var(--sp-2)}   /* 8  */
.stack-2{margin-top:var(--sp-4)}   /* 16 <- 14,16 */
.stack-3{margin-top:var(--sp-5)}   /* 20 <- 18,20,22 */
.stack-4{margin-top:var(--sp-6)}   /* 26 <- 24,26 */

/* Section spacing modifiers — .sec's own padding, adjusted. */
.sec--flush{padding-top:0}
.sec--gap{margin-bottom:var(--sp-8)}
.sec--gap-lg{margin-bottom:var(--sp-9)}
/* The mirror of .sec--gap, for a section whose own padding is zeroed out
   (.comp-sec) and that therefore lands too close to whatever precedes it. */
.sec--gap-top{margin-top:var(--sp-8)}

/* Spec list inside a ranking card. */
.rank-specs{margin-top:var(--sp-5);padding:0;list-style:none;font-size:var(--fs-sm);border-top:var(--frame-hair) solid var(--line)}
.rank-specs li{padding:7px 0;border-bottom:var(--frame-hair) solid var(--line)}

/* Brand row inside a .pcard (archive-sm_brand). */
.pcard__brandhead{display:flex;align-items:center;gap:14px;padding:20px 20px 0}
.pcard__logo{flex:none;width:64px;height:44px;display:flex;align-items:center;justify-content:center;background:var(--card);border:var(--frame) solid var(--line)}
.pcard__logo img{max-width:54px;max-height:34px;width:auto;height:auto;object-fit:contain}
.pcard__logo-fallback{font-family:var(--serif);font-weight:900;font-size:var(--fs-3xl)}
.pcard__brandtitle{font-size:19px;margin:0}
.pcard__news{font-size:var(--fs-sm);font-weight:700;color:var(--hi-2);margin:0 0 var(--sp-2)}
.pcard__stats{display:flex;gap:14px;flex-wrap:wrap;font-size:var(--fs-sm);color:var(--mute);font-weight:600;margin-top:6px}
.pcard__stats .is-live{color:var(--hi-2)}
.pcard--gone{opacity:.6}
/* the media box: .pcard__media already sizes <image-slot>, these cover the
   plain <img> and the no-image placeholder that were sized inline. */
.pcard__media img{display:block;width:100%;height:210px;object-fit:cover}
.pcard__media-ph{display:block;width:100%;height:210px;background:var(--hair)}

/* Editorial team card (front page). */
.teamcard{border:var(--frame) solid var(--ink);background:var(--card);box-shadow:var(--shadow-hi);padding:var(--sp-6);display:flex;gap:22px;align-items:flex-start;flex-wrap:wrap}
.teamcard__av{width:92px;height:92px;font-size:34px;flex:none}
.teamcard__body{flex:1;min-width:220px}
.teamcard__name{margin:0 0 2px}
.teamcard__name a{color:inherit}
.teamcard__role{font-family:var(--sans);font-size:var(--fs-xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--hi-2);margin:0 0 10px}
.teamcard__bio{font-size:var(--fs-base);line-height:1.6;color:var(--soft);margin:0 0 14px}

/* Callout on a dark surface (brand closure notice). */
/* No --shadow-hi (2026-08-21): that token is `6px 6px 0 var(--hi)`, and the
   live --hi is a light blue, so the "Nicht mehr erhältlich" / "Marke nicht mehr
   aktiv" notice sat on a hard pale-blue offset block. A dark alert box does not
   need a decorative drop block, and the colour read as a stray artefact rather
   than as emphasis. Same component both places, so both lose it. */
.callout{display:flex;gap:18px;align-items:flex-start;background:var(--dark);color:var(--on-dark);border:var(--frame-bold) solid var(--ink);padding:22px 26px;margin:var(--sp-7) 0 var(--sp-2)}
.callout__icon{font-size:var(--fs-5xl);line-height:1}
.callout__label{font-family:var(--sans);font-size:var(--fs-xs);font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--hi);margin:0 0 6px}
.callout__text{font-size:var(--fs-xl);line-height:1.55;margin:0}
.callout__text a{color:var(--hi);font-weight:700}

/* Affiliate CTA microcopy pair, used under coupon buttons on the brand and
   discount templates. */
.btn--note{font-size:var(--fs-xs);padding:11px}
.anz--block{display:block;margin-top:6px}

/* Badge colour variants — .stamp existed, its three colourways did not. */
.stamp--hi{background:var(--hi);color:var(--ink)}
.stamp--pos{background:var(--pos)}
.stamp--neg{background:var(--neg)}
.stamp--flush{align-self:flex-start;margin:0}

/* Author/byline avatar row, shared by author.php and single.php. */
.byline-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:var(--sp-5)}
.byline-row--lg{gap:26px;margin-top:var(--sp-4)}
.byline-row--lg .byline__av{width:104px;height:104px;font-size:40px;flex:none}

/* Radius, applied in one place. --radius is 0 by default so this changes
   nothing; the Customizer token page can raise it to preview rounded
   corners across the site without touching sixty rules. The border-radius:50%
   circles (dots, avatars, slider thumbs) are deliberately not in this list. */
/* .tbl-scroll is deliberately NOT in this list any more (2026-08-09): tables
   have no outer border to round, so the radius only clipped the corner cells. */
.surface,.surface--bold,.pcard,.bcard,.pc,.buycard,.deal,
.gallery figure,.verdict-box,.sidecard,.callout,.teamcard,
.dealcode,.dealnow,.stamp,.mega-feat,.review-flag,
input:not([type=range]),select,textarea{border-radius:var(--radius)}
/* A rounded card whose FIRST grid cell is a solid colour block: the child's
   square corners punch straight through the parent's rounded edge on the left
   (visible on the brand pages' discount rows, .deal__pct). Clipping the card is
   the fix that also survives the 760px breakpoint, where .deal__pct becomes a
   full-width row and it is the TOP corners that need it instead. */
.deal{overflow:hidden}
.btn,.nav-cta{border-radius:var(--radius-btn)}
/* …EXCEPT a cell in a shared-border grid, which cannot be rounded: its
   neighbours' straight edges butt against it, so with a radius set (the site
   runs --radius:20px) the side borders curve away into nothing at the card's
   open top and bottom. Standalone cards — .buycard, gallery tiles, the facet
   grid with its real gaps — keep the radius. Has to sit AFTER the rule above:
   same specificity, later wins. */
.grid-3 > *{border-radius:0}

/* ============================================================
   Product page — adoption pass 2026-08-12
   (design_handoff_produktseite: hero band §1, Unser Test §3,
   gallery collage §4). §2 / §5 / §6 are single-line edits made
   in place, up in the search, table and verdict blocks.

   No new tokens, colours or fonts: every value below is either a
   number the handoff names or an existing custom property. Where
   the handoff names --paper or --hair for something that has to
   READ as a light grey / a visible hairline, this uses
   --hair-soft instead — production overrides both of those to
   pure #fff via Design > Design tokens, and --hair-soft is the
   token that exists to survive exactly that.
   ============================================================ */

/* ---- §1 hero band ---- */
/* The band supplies its own vertical rhythm (crumbs 18px above, grid
   30px/62px), so the shared .hero padding — including its three responsive
   steps — is zeroed here rather than fought at each breakpoint. */
.hero--product{padding:0}

/* Breadcrumb, inside the band. Colours come from .crumbs--on-dark (dark bands)
   or the .crumbs light defaults; this only strips the standalone bar's surface
   and re-spaces it for the hero. */
.crumbs--hero{background:none;border-bottom:0}
.crumbs--hero .wrap{padding-top:18px;padding-bottom:0;font-size:12.5px;font-weight:500;flex-wrap:nowrap;overflow:hidden;white-space:nowrap}
.crumbs--hero a{font-weight:500}
/* Comparison heroes carry the trail in-band but, unlike .hero--product (which
   zeroes ALL .hero padding and re-pads its grid), they keep the shared .hero
   padding for their bottom edge. Only the top is swapped: the crumb row's 18px
   replaces it, and the margin restores the gap to the kicker/h1. The compound
   .hero.hero--crumbed selector outweighs the responsive .hero--compact padding
   shorthands further down, so the top stays put at every breakpoint. */
.hero.hero--crumbed{padding-top:0}
.hero--crumbed .crumbs--hero{margin-bottom:26px}

.hero--product .hero__grid{grid-template-columns:minmax(0,1fr) 430px;gap:56px;align-items:center;padding-top:30px;padding-bottom:62px}
.hero--product .hero__grid--single{grid-template-columns:minmax(0,1fr)}
.hero--product h1{font-size:54px;line-height:1.06;letter-spacing:-0.01em;margin:0 0 16px}
.hero--product h1 .it{font-weight:500;color:var(--hi)}
.hero--product .hero__lead-text{font-size:17.5px;line-height:1.65;max-width:540px;margin:0 0 28px}

/* Meta row: three value-over-kicker columns. Reuses .hero__meta's own
   `div b` + `div span` pattern (the one the brand and compact heroes use), so
   the kicker styling is inherited and only the sizes and the divider are new.
   The divider is on `+ div` rather than on nth-child, so a product with no
   score simply has no leading rule. */
.hero--product .hero__meta{display:flex;align-items:center;gap:14px 20px;flex-wrap:wrap;margin:0 0 32px}
.hero--product .hero__meta .hero__m + .hero__m{border-left:1px solid rgba(255,255,255,.18);padding-left:20px}
.hero__m{display:flex;flex-direction:column;gap:5px;white-space:nowrap}
/* Value + kicker, scoped to the two numeric columns: --by is a chip with its
   own spans inside, and must not inherit the kicker's uppercase treatment. */
.hero--product .hero__m--score b,.hero--product .hero__m--price b{font-family:var(--serif);font-weight:800;font-size:40px;line-height:1;color:#fff;display:block}
.hero--product .hero__m--score b small{font-size:16px;font-weight:inherit;color:var(--on-dark-soft)}
.hero--product .hero__m--score > span,.hero--product .hero__m--price > span{font-size:11.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase}
.hero--product .hero__m--score > span{color:var(--hi)}
.hero--product .hero__m--price b{font-weight:700;font-size:30px}
.hero--product .hero__m--price > span{color:var(--on-dark-soft)}
/* Author column: an inline chip, so it opts out of the stacked layout above.
   Its two inner spans also have to opt out of the kicker treatment that
   .hero__meta's own `div span` rule puts on EVERY span in the row (uppercase,
   .1em tracking, --mute, --fs-xs) — hence the reset, and hence the deeper
   selectors on the avatar and the name block so they outrank it. */
.hero--product .hero__m--by{flex-direction:row;align-items:center;gap:10px}
.hero--product .hero__meta .hero__m--by span{letter-spacing:normal;text-transform:none}
.hero--product .hero__meta .hero__m--by .hero__m-av{display:grid;place-items:center;width:38px;height:38px;flex:none;border-radius:50%;overflow:hidden;border:1px solid var(--hair);background:var(--hi);color:var(--ink);font-family:var(--serif);font-weight:700;font-size:16px}
.hero__m-av img{width:100%;height:100%;object-fit:cover}
.hero--product .hero__meta .hero__m--by .hero__m-who{display:flex;flex-direction:column;line-height:1.3;font-size:14px;color:var(--on-dark-soft)}
.hero--product .hero__m-who b{font-family:var(--sans);font-size:14px;font-weight:700;color:#fff}
.hero--product .hero__m-who small{font-size:12.5px;color:var(--on-dark-soft)}

/* CTA: .btn's own padding is already the handoff's 15px/24px, so only the
   fill, the radius, the soft shadow and the sky→steel hover are set here.
   border:0 keeps the box at exactly that padding. */
.hero--product .hero__cta{margin:0}
.hero--product .hero__cta .btn{gap:10px;border:0;font-size:16px;border-radius:12px;box-shadow:0 2px 8px rgba(17,35,47,.06)}
/* NO hover override here (2026-08-21): this CTA carries .btn--hi, so its hover
   is `.btn--hi:hover` — background:var(--ink). The local override painted it
   var(--hi-2) instead, which made the product hero the one CTA on the site that
   hovered a different colour from every other CTA. Shape (radius, shadow, no
   border) stays local; colour comes from the global class. */

/* Photo card: white surface + hairline + soft shadow, clipping a 330px crop.
   The frame and hard offset shadow the shared .hero__media img carries are
   dropped, so the card edge is the only line. */
.hero__media--card{background:var(--card);border:1px solid var(--hair-soft);border-radius:12px;overflow:hidden;box-shadow:0 10px 26px rgba(17,35,47,.09)}
.hero--product .hero__media--card img{height:330px;border:0;box-shadow:none;object-fit:cover}

@media (max-width: 980px){
  .hero--product .hero__grid{grid-template-columns:minmax(0,1fr) 340px;gap:36px}
}
@media (max-width: 880px){
  .hero--product .hero__grid{grid-template-columns:minmax(0,1fr);gap:32px;padding-bottom:48px}
  .hero--product h1{font-size:clamp(32px,6vw,44px)}
}
@media (max-width: 620px){
  .crumbs--hero .wrap{font-size:12px}
  .hero--product .hero__meta{gap:14px}
  .hero--product .hero__meta .hero__m + .hero__m{border-left:0;padding-left:0}
  .hero--product .hero__m--score b{font-size:32px}
  .hero--product .hero__m--price b{font-size:25px}
  .hero--product .hero__media--card img{height:240px}
}

/* ---- §3 Unser Test: measurement panels ---- */
/* Borderless white panels — no frame, no shadow, tighter padding. */
.testcard--plain{border:0;background:var(--card);padding:18px 20px;border-radius:12px;overflow:hidden}
.testcard--plain h4{font-size:11px;letter-spacing:.14em;color:var(--hi-2);margin:0 0 14px}
.testgrid:has(.testcard--plain){gap:20px;margin-bottom:28px}

/* Härtegrad gauge: bold label left, "N von 10" muted right, then a rounded
   track with the fill and a white dot marker riding its edge. */
.testcard--plain .firm__head{gap:14px;justify-content:space-between;margin-bottom:10px}
.testcard--plain .firm__head b{font-family:var(--sans);font-weight:700;font-size:16.5px}
.testcard--plain .firm__head span{font-weight:400;font-size:13px}
.testcard--plain .firm__track{height:10px;margin-right:4px;background:var(--hair-soft);border:0;border-radius:999px}
.testcard--plain .firm__fill{background:var(--hi-2);border-radius:999px}
.testcard--plain .firm__marker{width:22px;height:22px;background:#fff;border:0;box-shadow:0 2px 8px rgba(17,35,47,.22)}
.testcard--plain .firm__marker::after{content:"";width:10px;height:10px;border-radius:50%;background:var(--hi-2)}
.testcard--plain .firm__ends{margin-top:13px}
.testcard--plain .firm__ends span{font-size:12px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--mute)}

/* Eignung: the same rows, hairline empty dots instead of the 2px ink ring. */
.testcard--plain .suit__row{border-top-color:var(--hair-soft)}
.testcard--plain .suit .dot{width:13px;height:13px;border:1px solid var(--hair-soft)}
.testcard--plain .suit .dot.on{background:var(--hi-2);border-color:var(--hi-2)}

/* ---- §3 prose measure (this section only) ---- */
.prose--measure{font-size:17px;line-height:1.75;max-width:680px}
.prose--measure p{margin:0 0 18px}
.prose--measure > :last-child{margin-bottom:0}

/* ---- §4 gallery collage (Customizer: sm_gallery_layout = collage) ---- */
/* Gapless 3-column grid: the 1px "gaps" are the container's own colour showing
   through, which is why the grid has a background at all. */
.gallery-collage .gallery{grid-template-columns:repeat(3,1fr);grid-auto-rows:170px;gap:1px;background:var(--hair-soft);border:1px solid var(--hair-soft);border-radius:14px;overflow:hidden}
/* Cells cannot round inside a shared-border grid — same reason as .grid-3 > *. */
.gallery-collage .gallery figure{border-radius:0;border:0}
/* Photos past the tile stay in the DOM (and in the lightbox's list) but off the
   grid; the tile is what opens them. */
.gallery-collage .gallery .gallery__hidden{display:none}
.gallery__more .gallery__zoom{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-end;gap:4px;padding:16px;background:var(--hi);color:var(--ink);cursor:pointer;transition:background .15s,color .15s;text-align:left}
.gallery__more .gallery__zoom:hover{background:var(--hi-2);color:#fff}
.gallery__more-n{font-family:var(--serif);font-weight:800;font-size:26px;line-height:1}
.gallery__more-t{font-size:12.5px;font-weight:700;letter-spacing:.06em}
@media (max-width: 760px){
  .gallery-collage .gallery{grid-template-columns:repeat(2,1fr);grid-auto-rows:150px}
  .gallery-collage .gallery figure:first-child{grid-column:span 2;grid-row:span 1}
}

/* ============================================================
   Review sidebar — the "open" buy rail (2026-08-13)
   design_handoff_produktseite's sidebarOffen variant: no box, a
   left hairline, the score at display size. Show/hide per block
   lives in Customizer > Design > Review sidebar.

   The CTA is deliberately absent from this block: it keeps
   .btn--hi's bordered style, shared with the comparison page's
   sidebar CTA.
   ============================================================ */
/* No blanket `> * {border:0}` here: nothing bordered is left inside the rail
   (the coupon moved out to its own card, .buycard is no longer rendered), and
   such a rule would strip the star block's accent border when
   sm_star_layout = sidebar. */
.aside--open{gap:24px;border:0;border-left:1px solid var(--hair-soft);padding:4px 0 4px 26px}

.buyrail__row--logohead{align-self:flex-start}
.buyrail__logo{display:block;text-decoration:none}
.buyrail__logo img{display:block;width:auto;object-fit:contain}
/* min(100%,cap): the px cap tames wide wordmarks when there is room; the 100%
   lets the img follow its container when a flex row squeezes the logo (see the
   price-row rules below) — the logo then scales DOWN, keeping its aspect via
   object-fit:contain, rather than overflowing. */
.buyrail__logo--h26 img{height:26px;max-width:min(100%,150px)}
.buyrail__logo--h22 img{height:22px;max-width:min(100%,130px)}
.buyrail__logo--h19 img{height:19px;max-width:min(100%,120px)}
.buyrail__logo--h18 img{height:18px;max-width:min(100%,110px)}
/* Brand with no logo asset: the name carries the slot instead. */
.buyrail__brandtxt{font-weight:700;font-size:var(--fs-md);color:var(--ink)}

/* .buyrail__score / __num / __badge / __price are SHARED with the comparison
   rails (single-sm_comparison.php, both formats) since 2026-08-13 — the two
   rails must not drift. Both feed the price row from sm_price_html() as of
   2026-08-19, so the struck promo price below runs in all three rails. */
.buyrail__score{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.buyrail__num{display:flex;align-items:baseline;gap:2px}
.buyrail__num b{font-family:var(--serif);font-weight:800;font-size:54px;line-height:1}
.buyrail__num small{font-family:var(--serif);font-weight:700;font-size:19px;color:var(--mute)}
.buyrail__badge{background:var(--hi);color:var(--ink);font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:5px 11px;border-radius:999px}

/* Two explicit cells on row 1 — current price | brand — and the struck regular
   price on row 2 under the price it discounts.
   Why a grid and not the old flex row (fixed 2026-08-19): sm_price_html() emits
   "ab 519,99 € <s>1.039,98 €</s>", and at the rail's ~293px NOTHING fits both
   inline (a 30px serif four-digit price is already ~230px). In a flex row both
   prices were one item, so the struck price wrapped INSIDE it, the item grew to
   two lines, and align-items:center dropped the logo halfway between them — it
   straddled both lines instead of pairing with the price. The stack is the right
   layout at this width; it just has to be a real row, so the logo can stay
   pinned to row 1 and the row-2 price cannot move it. No magic offsets, so the
   430px font-size step below needs no counter-adjustment.
   Column sizing keeps the old give-way order: max-content = the price never
   wraps, minmax(0,auto) = the brand shrinks into what is left. */
.buyrail__price{display:grid;grid-template-columns:max-content minmax(0,auto);justify-content:space-between;align-items:center;column-gap:14px;row-gap:5px}
.buyrail__price > span:not(.buyrail__logo){font-family:var(--serif);font-weight:800;font-size:30px;line-height:1}
.buyrail__price .price-was{grid-area:2/1;justify-self:start;margin-left:0;font-size:18px;line-height:1.2}
/* The brand SHRINKS to the leftover space instead of wrapping under the price
   or overflowing the rail — both sidebars. Since the rail normalized to the
   aside's 320px track the rows almost always fit, but a four-digit price plus
   a 130px wordmark can still meet: the logo then scales down via its
   max-width:min(100%,cap), and the text fallback ellipsizes. */
.buyrail__price .buyrail__logo,
.buyrail__price .buyrail__brandtxt{grid-area:1/2;justify-self:end;min-width:0}
.buyrail__price .buyrail__brandtxt{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Buy-block rhythm = the product aside's uniform 24px flex gap (.aside--open),
   so the two sidebars read identically: score → price → CTA → coupon → certs
   all 24px apart. Sibling margins rather than a gap on .right__stick, because
   the rail also stacks pairs with deliberate TIGHT internal spacing (photo +
   gallery; CTA / "Anzeige" / "Testbericht lesen") that a container gap would
   blow apart. The score row itself takes no margin — the photo above it keeps
   its own 20px margin-bottom. The Format-A spec list carries its 24px inline
   (it needs its border-top padding either way). */
.right__stick > * + .buyrail__price,
.right__stick > .buyrail__score + .btn,
.right__stick > .buyrail__price + .btn,
.right__stick > * + .dealcode,
.right__stick > * + .rail-certs{margin-top:24px}

/* The disclosure keeps sitting tight under the button it labels, so the CTA and
   its notice are one block in the 24px rail rhythm rather than two. */
.buyrail__cta{display:flex;flex-direction:column}
.buyrail__provider{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--mute);margin:-8px 0 0}
.buyrail__provider--specs{justify-content:space-between;font-size:14px;margin:0 0 12px}

/* Coupon link — navigation to the brand's coupon page, not a code hand-off.
   Same live dot as .dealnow__dot / .nav-cta__dot, so "there is something current
   behind this" reads identically everywhere on the site. */
.buyrail__coupon{display:flex;align-items:center;justify-content:center;gap:8px;font-size:13.5px;font-weight:700;color:var(--ink);transition:color .15s}
.buyrail__coupon:hover{color:var(--hi-2)}
.buyrail__dot{position:relative;width:8px;height:8px;border-radius:50%;background:var(--neg);flex:none}
.buyrail__dot::after{content:"";position:absolute;inset:0;border-radius:50%;background:var(--neg);animation:sm-livepulse 1.8s ease-out infinite}
@media (prefers-reduced-motion: reduce){.buyrail__dot::after{animation:none}}

.buyrail__specs{border-top:1px solid var(--hair-soft);padding-top:16px}
.specgrid{display:grid;grid-template-columns:1fr 1fr;gap:16px 12px}
.specgrid__item{display:flex;align-items:flex-start;gap:10px;min-width:0}
.specgrid__ic{flex:none;width:20px;height:20px;margin-top:1px;color:var(--hi-2)}
.specgrid__txt{display:flex;flex-direction:column;gap:1px;min-width:0}
.specgrid__txt b{font-size:13.5px;font-weight:700;color:var(--ink);line-height:1.3}
.specgrid__txt small{font-size:12px;color:var(--mute);line-height:1.35}

/* Certifications: the comparison rail's component, so the two sidebars show
   seals the same way. --solo already supplies the top spacing. */
.aside--open .rail-certs--solo{margin-top:0}

/* Coupon card — a real card below the borderless rail, which is the one place
   in this column that still wants an edge. Its own block, because handing over
   a code is a different job from linking to the brand's coupon page. */
.railcoupon{background:var(--card);border:1px solid var(--hair-soft);border-radius:12px;box-shadow:0 4px 12px rgba(17,35,47,.07);padding:14px 16px}
.railcoupon__head{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.railcoupon__head b{font-size:13.5px;font-weight:700;color:var(--ink)}
.railcoupon__head small{margin-left:auto;font-size:11px;color:var(--mute)}
/* The dealcode fills the card and takes the handoff's lighter dashed box here.
   SCOPED on purpose — .dealcode is a global component (comparison rows, brand
   pages, the star block) and its 2px ink dashes stay as they are everywhere else. */
.railcoupon .dealcode--slim{width:100%;border-width:1px;border-color:var(--hair-soft);border-radius:8px}
/* A coupon whose title carries no "N %" has no .dealcode__pct, and the slim
   variant keeps the masked code hidden until it is revealed — so without this
   the row was a wide blank gap with the button shoved to the right. Same answer
   the other narrow rail already uses (.right__stick): the button takes the row.
   The card's own head already names the brand, so nothing is lost by not
   printing a label here. */
.railcoupon .dealcode--slim:not(:has(.dealcode__pct)) .dealcode__act{margin-left:0;width:100%}
.railcoupon .dealcode--slim:not(:has(.dealcode__pct)) .dealcode__btn{flex:1 1 auto;text-align:center;padding:9px 12px}

@media (max-width: 980px){
  /* The rail is a full-width block once .with-aside collapses — a left rule
     down the side of the page would read as a stray vertical line there. */
  .aside--open{border-left:0;padding:0;gap:20px}
  /* Centre the score row and keep the coupon link centred (Tim, 2026-08-21).
     The coupon USED to be forced to flex-start here, which left it and the
     score hanging on the left of a now-full-width block while the CTA below
     them was centred. .buyrail__coupon's own default is already centre, so the
     override just had to go; .buyrail__score has no default, hence the rule.
     Scoped to .aside--open: __score/__num/__badge are SHARED with the
     comparison rail (.right__stick), which is not full width and must not
     centre. */
  .aside--open .buyrail__score{justify-content:center}
}
@media (max-width: 430px){
  .buyrail__num b{font-size:44px}
  .buyrail__price > span:not(.buyrail__logo){font-size:26px}
  .buyrail__price .price-was{font-size:16px}
  /* NO .specgrid{grid-template-columns:1fr} here any more (Tim, 2026-08-21):
     the spec tiles stay 2-up on a phone, as on desktop. Once .with-aside
     collapses the rail is full width (358px at 390px viewport), so each column
     is ~173px — WIDER than the same column in the 300px desktop rail. Dropping
     to one column made the block twice as tall for no gain. */
}

/* ── Gutenberg core blocks inside .prose (2026-09-09, FR migration) ──────────
   The French market (quelmeilleurmatelas.com) brings 133 articles written in
   Gutenberg — columns, buttons, media-text, galleries, tables, Yoast FAQ — that
   now render inside .prose (single.php, page.php, the comparison body). The DE
   articles were classic-editor HTML, so nothing here had a rule before.

   What the front end loads (inc/setup.php does NOT dequeue anything):
   - wp-block-library (block-library/style.css, whole file, in <head>) — the
     layout rules for columns, media-text, gallery widths, embed ratios.
   - global-styles inline (theme.json exists) — preset vars, `.is-layout-flex`,
     `:root :where(.wp-element-button,.wp-block-button__link){…#32373c…}`.
   - core-block-supports inline (footer) — per-instance layout classes
     (`.wp-container-core-columns-is-layout-…{flex-wrap:nowrap}`).
   - classic-theme-styles is NOT loaded (skipped when a theme.json exists).
   So this section is written as OVERRIDES: every selector is scoped to .prose
   (one extra class) so it outranks core's own rules; `!important` appears only
   where core itself uses it (columns flex-wrap / flex-basis, old gallery
   widths) or where an author's inline style would break the design (button
   radius).

   theme.json switches off the default palette and custom font sizes, so the
   WP default colour classes carried by the imported buttons
   (.has-vivid-purple-background-color …) resolve to nothing — the buttons
   fall through to the --btn-* tokens below. Inline `style="background:…"`
   gradients/colours on columns are the author's and are kept.

   Breakpoints: 760 (columns + media-text stack) and 620 (gallery 1-col,
   spacer cap) — two of the five, no new ones. Literal px below are one-offs
   noted inline; everything else is a token. */

/* -- generic Gutenberg utilities ------------------------------------------ */
.prose .has-text-align-center{text-align:center}
.prose .has-text-align-right{text-align:right}
.prose .has-text-align-left{text-align:left}
/* alignwide/alignfull have no room to grow inside the 760px article column —
   clamp them to the prose width instead of letting them bleed into the aside. */
.prose .alignwide,.prose .alignfull{max-width:100%;width:auto;margin-left:0;margin-right:0}
/* The imported content uses WP's DEFAULT font-size slugs (small/medium/large/
   x-large), which theme.json does not define, so global-styles emits no rule
   for them. Map them onto the ramp so "small print" paragraphs read as such. */
.prose .has-small-font-size{font-size:var(--fs-base)}
.prose .has-medium-font-size{font-size:var(--fs-3xl)}
.prose .has-large-font-size{font-size:var(--fs-4xl)}
.prose .has-x-large-font-size{font-size:var(--fs-5xl)}
/* Shared caption look: image, table, embed, gallery (the nested gallery keeps
   core's overlay caption — its selector outranks this one on purpose). */
.prose figcaption.wp-element-caption,.prose .wp-block-image figcaption,.prose .wp-block-table figcaption,.prose .wp-block-embed figcaption{display:block;font-size:var(--fs-sm);color:var(--mute);font-weight:600;line-height:1.5;margin:var(--sp-2) 0 0;padding-left:2px;text-align:left}
.prose .wp-block-image.aligncenter figcaption,.prose .has-text-align-center figcaption{text-align:center;padding-left:0}
/* Group / columns container with an author background: token padding
   (core: 1.25em 2.375em). */
.prose .wp-block-group.has-background,.prose .wp-block-columns.has-background{padding:var(--sp-6)}

/* -- columns -------------------------------------------------------------- */
/* Core: display:flex; flex-wrap:wrap!important below 782, nowrap!important
   above; every column flex-basis:100%!important below 782. We move the stack
   point to the theme's 760 breakpoint. Between 761 and 781 core still forces
   flex-basis:100% on each column, but with nowrap the columns simply shrink
   equally — so no sixth breakpoint is needed to bridge the 21px window.
   Multi-column text runs one step smaller than the 19px prose: three 240px
   columns at 19px are ~15 characters wide. */
.prose .wp-block-columns{display:flex;flex-wrap:nowrap!important;gap:var(--sp-5);margin:0 0 var(--sp-6);font-size:var(--fs-lg);line-height:1.6}
.prose .wp-block-column{flex:1 1 0;min-width:0;margin:0}
.prose .wp-block-column[style*=flex-basis]{flex-grow:0}
/* Compact rhythm inside a column: the 26px paragraph / 48px heading margins
   of the article body are far too loose in a card. */
.prose .wp-block-column > :first-child{margin-top:0}
.prose .wp-block-column > :last-child{margin-bottom:0}
.prose .wp-block-column p{margin-bottom:var(--sp-4)}
.prose .wp-block-column h2,.prose .wp-block-column h3,.prose .wp-block-column h4{margin:var(--sp-4) 0 var(--sp-3);line-height:1.15}
.prose .wp-block-column ul,.prose .wp-block-column ol{margin:0 0 var(--sp-4);padding-left:var(--sp-5)}
.prose .wp-block-column li{margin-bottom:var(--sp-1)}
.prose .wp-block-column .wp-block-buttons{margin-bottom:var(--sp-3)}
.prose .wp-block-column hr.wp-block-separator{margin:var(--sp-4) auto}
/* Column used as a card ("Notre top 3"): the gradient/colour is inline and
   stays; padding from tokens (an inline padding, if the author set one, wins
   — that is what inline means). */
.prose .wp-block-column.has-background{padding:var(--sp-6);border-radius:var(--radius)}
/* Images in a column: never stretched to the column width — a product cut-out
   PNG sits at its own size, centred. In a card it also drops the 2px photo
   frame (.prose img) and is capped so three cards line up whatever the source
   dimensions. 220px: one-off cap, ~the height of a card's text block. */
.prose .wp-block-column .wp-block-image{margin:var(--sp-4) 0}
.prose .wp-block-column .wp-block-image img{width:auto;max-width:100%;margin:0 auto}
.prose .wp-block-column.has-background .wp-block-image img{border:0;max-height:220px;object-fit:contain;background:transparent}
@media (max-width: 760px){
  .prose .wp-block-columns:not(.is-not-stacked-on-mobile){flex-wrap:wrap!important;gap:var(--sp-4)}
  .prose .wp-block-columns:not(.is-not-stacked-on-mobile) > .wp-block-column{flex-basis:100%!important;flex-grow:1}
  /* Empty columns only exist to centre a solo card on desktop (Latexbio);
     stacked, they would just be two blank gaps. */
  .prose .wp-block-columns:not(.is-not-stacked-on-mobile) > .wp-block-column:empty{display:none}
}

/* -- buttons -------------------------------------------------------------- */
/* .wp-block-button__link styled like .btn, in the theme's CTA voice
   (uppercase, tracked, --fs-sm). Built from the --btn-* tokens so the
   Customizer's button preset restyles these too. */
.prose .wp-block-buttons{display:flex;flex-wrap:wrap;gap:var(--sp-3);margin:0 0 var(--sp-6)}
.prose .wp-block-buttons.is-content-justification-center{justify-content:center}
.prose .wp-block-buttons.is-content-justification-right{justify-content:flex-end}
.prose .wp-block-buttons.is-content-justification-space-between{justify-content:space-between}
.prose .wp-block-buttons.is-vertical{flex-direction:column}
.prose .wp-block-buttons > .wp-block-button{margin:0}
/* No .prose a yellow underline on a button, nor on the image link of a card. */
.prose .wp-block-buttons a,.prose .wp-block-button__link,.prose .wp-block-image > a{box-shadow:none}
.prose .wp-block-button .wp-block-button__link{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--sans);font-weight:700;font-size:var(--fs-sm);letter-spacing:.08em;text-transform:uppercase;line-height:1.25;text-align:center;text-decoration:none;background:var(--btn-bg);color:var(--btn-fg);padding:14px 22px;border:var(--frame) solid var(--btn-border);cursor:pointer;transition:.15s}
/* The design is square (--radius-btn). The imported buttons carry an inline
   border-radius:16px/26px from the old theme; !important is the only way to
   outrank an inline style, and here it is the token that must win. */
.prose .wp-block-button .wp-block-button__link{border-radius:var(--radius-btn)!important}
.prose .wp-block-button .wp-block-button__link:hover{background:var(--btn-hover-bg);color:var(--btn-hover-fg)}
/* Core sets font-size:inherit on .has-custom-font-size buttons at (0,4,0) —
   this has to be at least as specific to keep the CTA size. */
.prose .wp-block-buttons > .wp-block-button.has-custom-font-size .wp-block-button__link{font-size:var(--fs-sm)}
/* Outline style = .btn--o: transparent, ink border, fills on hover. The
   unresolvable palette classes on the imported buttons are ignored here. */
.prose .wp-block-button.is-style-outline .wp-block-button__link{background:transparent;background-image:none;color:var(--ink);border-color:var(--ink)}
.prose .wp-block-button.is-style-outline .wp-block-button__link:hover{background:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border)}
/* Full-width button (wp-block-button__width-100): core already sets the
   wrapper to 100%; the link follows. */
.prose .wp-block-buttons > .wp-block-button.has-custom-width .wp-block-button__link{width:100%}

/* -- image ---------------------------------------------------------------- */
/* The figure carries the rhythm; the img inside loses the 30px .prose img
   margin but keeps its 2px frame (editorial photo). */
.prose figure.wp-block-image{margin:var(--sp-7) 0;max-width:100%}
.prose .wp-block-image img{margin:0;height:auto}
.prose .wp-block-image > a{display:block}
/* Centred figure: core makes it display:table; a block with an auto-margin
   img is the same result without the table quirks (caption as table-caption). */
.prose .wp-block-image.aligncenter{display:block}
.prose .wp-block-image.aligncenter img{width:auto;max-width:100%;margin-left:auto;margin-right:auto}
/* Floats reuse .prose .alignleft/.alignright (max-width:50%, released ≤760). */
.prose .wp-block-image.alignleft img,.prose .wp-block-image.alignright img{width:100%}

/* -- media & text --------------------------------------------------------- */
.prose .wp-block-media-text{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-6);align-items:center;margin:var(--sp-7) 0}
.prose .wp-block-media-text.has-media-on-the-right{grid-template-columns:1fr 1fr}
.prose .wp-block-media-text .wp-block-media-text__media{margin:0;min-width:0;grid-row:1;grid-column:1}
.prose .wp-block-media-text .wp-block-media-text__content{padding:0;min-width:0;grid-row:1;grid-column:2}
.prose .wp-block-media-text.has-media-on-the-right .wp-block-media-text__media{grid-column:2}
.prose .wp-block-media-text.has-media-on-the-right .wp-block-media-text__content{grid-column:1}
.prose .wp-block-media-text .wp-block-media-text__content > :last-child{margin-bottom:0}
.prose .wp-block-media-text .wp-block-media-text__media img{display:block;width:100%;height:auto;margin:0}
.prose .wp-block-media-text.is-image-fill .wp-block-media-text__media{min-height:250px} /* core's own fill min-height, restated at our specificity */
@media (max-width: 760px){
  /* Stacked at the theme breakpoint (core waits until 600), media first.
     Every media-text stacks here — a 50/50 split in a ≤760 column is not
     readable, whatever the block's is-stacked-on-mobile flag says. */
  .prose .wp-block-media-text,.prose .wp-block-media-text.has-media-on-the-right{grid-template-columns:100%;gap:var(--sp-4)}
  .prose .wp-block-media-text .wp-block-media-text__media,.prose .wp-block-media-text.has-media-on-the-right .wp-block-media-text__media{grid-column:1;grid-row:1}
  .prose .wp-block-media-text .wp-block-media-text__content,.prose .wp-block-media-text.has-media-on-the-right .wp-block-media-text__content{grid-column:1;grid-row:2}
}

/* -- separator ------------------------------------------------------------ */
/* Hairline in --line. A literal 1px, not --frame-hair: production runs
   --frame-hair:0 as a token override (see the --hair-soft note in :root) and
   a separator that can vanish is no separator. Default = core's short centred
   rule (100px, one-off), .is-style-wide / alignwide / alignfull = full width. */
.prose hr.wp-block-separator{border:0;border-top:1px solid var(--line);height:0;width:100px;max-width:100%;margin:var(--sp-7) auto;background:none;opacity:1;clear:both}
.prose hr.wp-block-separator.is-style-wide,.prose hr.wp-block-separator.alignwide,.prose hr.wp-block-separator.alignfull{width:100%}
.prose hr.wp-block-separator.is-style-dots{border:0;height:auto;width:auto;line-height:1;text-align:center}
.prose hr.wp-block-separator.is-style-dots::before{content:"···";color:var(--mute);font-family:var(--serif);font-size:var(--fs-4xl);letter-spacing:2em;padding-left:2em}

/* -- spacer --------------------------------------------------------------- */
/* Height is inline from the editor; on a phone anything past --sp-8 is dead
   scroll. */
.prose .wp-block-spacer{clear:both;margin:0}
@media (max-width: 620px){.prose .wp-block-spacer{max-height:var(--sp-8)}}

/* -- table ---------------------------------------------------------------- */
/* Mapped onto the .tbl look: row hairlines in --nav-line (the divider colour
   that survives the token overrides, same as .tbl), th as an uppercase kicker
   on --paper, a frame under the header row. The figure is the scroll box. */
.prose figure.wp-block-table{margin:var(--sp-7) 0;max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:var(--ink) var(--hair)}
.prose .wp-block-table table{width:100%;border-collapse:collapse;border:0;font-size:var(--fs-base);line-height:1.45}
.prose .wp-block-table thead,.prose .wp-block-table tfoot{border:0}
.prose .wp-block-table th{font-family:var(--sans);font-size:var(--fs-2xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;text-align:left;color:var(--ink);background:var(--paper);padding:13px 13px;border:0;border-bottom:var(--frame) solid var(--line);vertical-align:bottom}
.prose .wp-block-table td{padding:12px 13px;border:0;border-bottom:1px solid var(--nav-line);vertical-align:top;color:var(--prose-ink)}
.prose .wp-block-table tbody tr:last-child td{border-bottom:0}
.prose .wp-block-table tfoot td{border-top:var(--frame) solid var(--line);border-bottom:0;font-weight:700}
/* Stripes style: white rows on the paper ground instead of core's #f0f0f0. */
.prose .wp-block-table.is-style-stripes{border-bottom:0}
.prose .wp-block-table.is-style-stripes tbody tr:nth-child(odd){background:var(--card)}
.prose .wp-block-table.is-style-stripes td,.prose .wp-block-table.is-style-stripes th{border-color:var(--nav-line)}
.prose .wp-block-table .has-fixed-layout{table-layout:fixed}
@media (max-width: 760px){
  .prose .wp-block-table table{font-size:var(--fs-md)}
  .prose .wp-block-table th{padding:11px 10px}
  .prose .wp-block-table td{padding:12px 10px}
}

/* -- gallery -------------------------------------------------------------- */
/* Core does the columns (2 below 600, 3 = columns-default above) and the
   overlay captions; we add the rhythm, the photo frame and a single column on
   phones. Two markups exist: figure.wp-block-gallery.has-nested-images (WP ≥
   5.9) and the old ul.wp-block-gallery / .blocks-gallery-grid list. */
.prose .wp-block-gallery,.prose .blocks-gallery-grid{margin:var(--sp-7) 0;padding:0;list-style:none}
.prose .wp-block-gallery figure.wp-block-image,.prose .blocks-gallery-item figure{margin:0}
.prose .wp-block-gallery img{margin:0;border:var(--frame) solid var(--line)}
.prose .wp-block-gallery a{box-shadow:none}
.prose .blocks-gallery-item{margin-bottom:0}
@media (max-width: 620px){
  /* Nested markup: core sizes items with a :not(#individual-image) selector
     (ID-level specificity), so the override has to carry the same hook. */
  .prose .wp-block-gallery.has-nested-images figure.wp-block-image:not(#individual-image){width:100%}
  /* Old list markup: core's nth-of-type(Nn) margin rules outrank a plain
     class chain — !important is the honest override here. */
  .prose .wp-block-gallery:not(.has-nested-images) .blocks-gallery-item,.prose .blocks-gallery-grid:not(.has-nested-images) .blocks-gallery-item{width:100%!important;margin-right:0!important}
}

/* -- quote ---------------------------------------------------------------- */
/* Same voice as .prose blockquote (serif italic, --hi rule), one step down
   from its --fs-4xl: the imported quotes are asides, not pull quotes. */
.prose .wp-block-quote{margin:var(--sp-7) 0;padding:6px 0 6px 24px;border-left:5px solid var(--hi);font-family:var(--serif);font-style:italic;font-size:var(--fs-2xl);line-height:1.5}
.prose .wp-block-quote p{font-size:inherit;line-height:inherit;color:var(--ink);margin:0 0 var(--sp-3)}
.prose .wp-block-quote p:last-of-type{margin-bottom:0}
.prose .wp-block-quote cite{display:block;margin-top:var(--sp-3);font-family:var(--sans);font-style:normal;font-weight:600;font-size:var(--fs-sm);letter-spacing:.03em;color:var(--mute)}
.prose .wp-block-quote.is-style-plain{border-left:0;padding-left:0}
.prose .wp-block-quote.has-text-align-center{border-left:0;padding-left:0;text-align:center}
.prose .wp-block-quote.is-large p,.prose .wp-block-quote.is-style-large p{font-size:var(--fs-3xl)}

/* -- embed ---------------------------------------------------------------- */
/* Core (responsive-embeds is on) handles .wp-has-aspect-ratio with a padding
   ::before + absolute iframe. This adds the rhythm and a 16:9 box for a video
   embed that arrived WITHOUT the aspect classes. */
.prose .wp-block-embed{margin:var(--sp-7) 0;max-width:100%}
.prose .wp-block-embed .wp-block-embed__wrapper{position:relative;max-width:100%}
.prose .wp-block-embed iframe{display:block;max-width:100%;border:0}
.prose .wp-block-embed.is-type-video:not(.wp-has-aspect-ratio) .wp-block-embed__wrapper{aspect-ratio:16/9}
.prose .wp-block-embed.is-type-video:not(.wp-has-aspect-ratio) .wp-block-embed__wrapper iframe{position:absolute;inset:0;width:100%;height:100%}

/* -- Yoast FAQ block (yoast/faq-block) ------------------------------------ */
/* Markup: .schema-faq > .schema-faq-section > strong.schema-faq-question +
   p.schema-faq-answer. Each section is a .surface-style card (2px --line
   frame on --card); the question is a serif subhead, the answer runs in
   --soft. (Comparisons have the block lifted into comparison_faqs and
   rendered by the template; this is for the 78 articles that keep it.) */
.prose .schema-faq{display:grid;gap:var(--sp-3);margin:var(--sp-7) 0}
.prose .schema-faq-section{border:var(--frame) solid var(--line);background:var(--card);border-radius:var(--radius);padding:var(--sp-5) var(--sp-6)}
.prose .schema-faq-question{display:block;font-family:var(--serif);font-weight:800;font-size:var(--fs-3xl);line-height:1.25;letter-spacing:-0.01em;color:var(--ink);margin:0 0 var(--sp-2)}
.prose .schema-faq-answer{margin:0;color:var(--soft);font-size:var(--fs-xl);line-height:1.6}
.prose .schema-faq-section > :last-child{margin-bottom:0}
/* ── end Gutenberg core blocks inside .prose ── */
