/*
 * Futura PT — ORWO brand font, loaded from live ORWO with CORS=*.
 * The merged ORWO bundle declares font-family: "Futura PT …" but doesn't
 * include @font-face (those live in a separate TYPO3-managed bundle that
 * sometimes 404s by hash). We define them here so the preview matches live
 * regardless of which TYPO3 bundles got mirrored.
 */
@font-face {
  font-family: 'Futura PT Book';
  font-display: swap;
  src: url('https://www.orwo.de/_assets/50274901be30f02a74e08c8b9baa9a4e/orwo/fonts/futura-pt_book/futura-pt_book.woff') format('woff');
}
@font-face {
  font-family: 'Futura PT Bold';
  font-display: swap;
  src: url('https://www.orwo.de/_assets/50274901be30f02a74e08c8b9baa9a4e/orwo/fonts/futura-pt_bold/futura-pt_bold.woff') format('woff');
}
@font-face {
  font-family: 'Futura PT Condensed Bold';
  font-display: swap;
  src: url('https://www.orwo.de/_assets/50274901be30f02a74e08c8b9baa9a4e/orwo/fonts/futura-pt_cond-bold.woff') format('woff');
}

/*
 * cf-landing.css — scoped styles for factory-rendered marketing landings.
 *
 * Loaded by landing-factory.xhtml. Deployed to
 * /_default/landing-content/_layouts/cf-landing.css.
 *
 * All rules are scoped under `.cf-landing.cf-marketing` so they don't leak
 * into hand-authored shop pages. Bootstrap 5 utility classes carry layout;
 * this stylesheet only adds the ORWO-specific patterns we need on top:
 *   - element-header (heading underline pattern)
 *   - orwo-highlight + bg-primary App-teaser block
 *   - .cf-landing accordion-button cursor (matches preview workaround)
 *   - PDP basic + basket + delivery layout primitives
 *   - recommended-card hover affordance
 *
 * No inline style="…" attributes in factory output — they're stripped by the
 * sanitiser. Theme-specific classes (frame-default, accordion-*, btn-*,
 * orwo-btn-primary) are reused unchanged from orwo-bootstrap.css.
 */

/* ORWO brand primary (#06acda = rgb(6, 172, 218)). Bootstrap 5.3.3 from CDN
 * defaults to #0d6efd; the merged ORWO bundle should override it but in our
 * preview the cascade order can let Bootstrap win. Re-asserting at the
 * .cf-landing scope guarantees `.bg-primary`, `.btn-primary`, `.text-primary`
 * etc. render in ORWO turquoise locally just like live. */
.cf-landing.cf-marketing {
  --bs-primary: #06acda;
  --bs-primary-rgb: 6, 172, 218;
}

/* One of the merged ORWO bundles paints `body { background: #fff
 * url(data:image/gif;base64,…dark-pixel) }` — meant for the live admin
 * shell, not us. On pages where that bundle gets discovered, the dark gif
 * tiles across the entire viewport making text unreadable in our preview.
 * Live ORWO's chrome (header + content frames) covers it; our preview
 * doesn't ship the chrome, so we force a clean white body for parity. */
body {
  background: #fff !important;
}

/* Bootstrap's `.row` carries `margin: 0 -.75rem` (negative gutters that the
 * matching `.container` cancels). Live wraps `<article class="product row">`
 * inside such a container; we don't, so the negative margin pokes 15px
 * past the viewport on mobile and creates a horizontal scroll bar. Reset
 * the inline margins on our top-level rows. */
.cf-landing.cf-marketing > .aimeos.catalog-detail > article.product.row,
.cf-landing.cf-marketing > section.aimeos.catalog-stage.row {
  margin-left: 0;
  margin-right: 0;
}

/* Belt-and-suspenders against any other rule that might still cause
 * horizontal overflow on narrow viewports. */
.cf-landing.cf-marketing {
  overflow-x: hidden;
}

/* Sticky scroll-config bar (live ORWO #scrollConfig). Hidden initially;
 * a tiny inline script flips `.is-visible` on once the user scrolls past
 * the hero PDP. Shown on ALL viewports — live ORWO renders this sticky
 * price + CTA bar on mobile too (it was previously desktop-only here, which
 * diverged from live; restored 2026-06-09). */
.cf-landing.cf-marketing #scrollConfig.cf-scroll-config {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 9999;
  /* Live ORWO measured: rgb(222, 222, 222) — light grey bar. */
  background: rgb(222, 222, 222);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
  padding: 0.75rem 2rem;
  transform: translateY(100%);
  transition: transform 0.25s ease;
}

.cf-landing.cf-marketing #scrollConfig.cf-scroll-config.is-visible {
  transform: translateY(0);
}

.cf-landing.cf-marketing #scrollConfig .aimeos .product {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  /* Full width per user request 2026-05-06 — earlier 1320px max-width
   * left visible margins on ultra-wide viewports; user prefers the bar
   * stretch fully and rely on padding-right for breathing room. */
  max-width: 100%;
  margin: 0 auto;
  /* Right-edge breathing room — keeps the CTA button off the viewport
   * edge on wide screens. */
  padding-right: 3vw;
}

.cf-landing.cf-marketing #scrollConfig .price-item.default .value {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  font-family: 'Futura PT Condensed Bold', Arial, Verdana, Helvetica;
  /* 2rem per user request 2026-05-06 — bumped from 1.8rem so the price
   * weighs equally with the CTA button in the sticky bar. */
  font-size: 2rem;
  font-weight: 600;
}

.cf-landing.cf-marketing #scrollConfig .price-item .taxrate {
  font-size: 0.75rem;
  color: #6c757d;
  margin-left: 0.5rem;
}

.cf-landing.cf-marketing #scrollConfig .orwo-btn-primary {
  white-space: nowrap;
}

/* Mobile: tighten the bar so the price + CTA fit a narrow viewport (the desktop
 * 2rem padding / 2rem price are too wide at ~390px). Mirrors live ORWO's
 * compact mobile sticky bar. */
@media (max-width: 767px) {
  .cf-landing.cf-marketing #scrollConfig.cf-scroll-config {
    padding: 0.5rem 1rem;
  }
  .cf-landing.cf-marketing #scrollConfig .aimeos .product {
    gap: 0.5rem;
    padding-right: 0;
  }
  .cf-landing.cf-marketing #scrollConfig .price-item.default .value {
    font-size: 1.5rem;
  }
}

.cf-landing.cf-marketing .element-header {
  margin-top: 3rem;
  margin-bottom: 1.5rem;
}

/* Live ORWO renders the product title in all-caps via CSS, not in the markup
 * (HTML has "Fotokacheln", display shows "FOTOKACHELN"). Mirror that with
 * text-transform so the factory's lowercase frontmatter still produces the
 * expected look. Verified on /detail/fotokacheln 2026-05-06. Font-family +
 * font-size set per user request to match live ORWO's product-title scale. */
.cf-landing.cf-marketing .product-title {
  text-transform: uppercase;
  font-family: 'Futura PT Condensed Bold', Arial, Verdana, Helvetica, sans-serif;
  font-size: 2.2rem;
}

/* "Mehr erfahren" link in the PDP description column — live ORWO renders
 * this at the same size as surrounding bullet text. The `.orwo-cond` class
 * elsewhere bumps font-size; we reset to 100% (= parent / inherited size)
 * so the link doesn't visually dominate the bullet list. */
.cf-landing.cf-marketing .more-description .orwo-link {
  font-size: 100%;
}

/* Bullet list in the PDP description column — font-size set to 100% per
 * user request 2026-05-06 (inherits from parent). Earlier 0.95rem was
 * tried to keep "Materialstärke: 5 mm" on one line but the user prefers
 * inheriting the natural body size; if the wrap reappears it can be
 * addressed via column-width tweaks instead. */
.cf-landing.cf-marketing .long-description ul li {
  font-size: 100%;
  line-height: 1.4;
}

.cf-landing.cf-marketing .element-header > span {
  display: inline-block;
  position: relative;
}

.cf-landing.cf-marketing .article-body p {
  line-height: 1.6;
  margin-bottom: 1rem;
}

.cf-landing.cf-marketing .article-body img {
  max-width: 100%;
  height: auto;
  display: block;
}

.cf-landing.cf-marketing .orwo-highlight,
.cf-landing.cf-category-landing .orwo-highlight {
  margin-top: 2rem;
  margin-bottom: 2rem;
  border-radius: 4px;
  overflow: hidden;
}

/* Restore Bootstrap's `.col-md-6 { width: 50% }` on category-landing highlight
 * rows that carry both `.row` and `.col-md-6` classes. orwo-desktop.min.css
 * ships legacy Bootstrap-3-era `.row { width: 1020px }` / `.row { width: auto }`
 * declarations that load after orwo-bootstrap.css and outrank `.col-md-6` in
 * the cascade. Two-class scope (`.cf-landing.cf-category-landing`) gives us
 * higher specificity than the offending `.row` rule. Live ORWO's TYPO3 stack
 * doesn't ship those legacy rules, so it works there. */
@media (min-width: 768px) {
  .cf-landing.cf-category-landing .row.orwo-highlight.col-md-6 {
    width: 50%;
  }
}

/* Live ORWO renders the catalog-lists-content-after highlight blocks edge to
 * edge with no gap between them. The base `.orwo-highlight` rule above sets
 * 2rem top/bottom margins (matches PDPs); on category landings we zero them
 * out so the FOTO APP / ANALOG / FOTO TIPP / "Passendes Zubehör" strip
 * flows as one continuous visual band. `!important` guards against the
 * shop's orwo-desktop.min.css picking up `.row` margin rules later. */
.cf-landing.cf-category-landing .row.orwo-highlight.bg-tertiary {
  margin-bottom: 0 !important;
}
.cf-landing.cf-category-landing .row.orwo-highlight.col-md-6 {
  margin: 0 !important;
}

/* Restore Bootstrap's default `--bs-gutter-x: 40px` on category-landing rows.
 * orwo-bootstrap.css ships a second `.row { --bs-gutter-x: 0 }` declaration
 * that kills `.row > * { padding-right: calc(var(--bs-gutter-x) * .5) }`,
 * leaving the col-md-6 children with no side padding. orwo-desktop.min.css
 * also drops it to 10px !important on any `.container` element, so rows that
 * also carry `.container` (image+text pairs, bg-tertiary/bg-light highlights)
 * need an explicit !important override at higher specificity to win the
 * cascade. On live ORWO (TYPO3 stack, different CSS) the col-md-6 ships
 * with 20px horizontal padding — that's the visible gap between image and
 * text columns in image+text pair blocks. Match it. */
.cf-landing.cf-category-landing .row,
.cf-landing.cf-category-landing .row.container {
  --bs-gutter-x: 40px !important;
}

.cf-landing.cf-marketing .accordion-button {
  cursor: pointer;
}

/* Hide the .badges block on the PDP main teaser. Aimeos paints these `Neu`
 * and `Sale` badges from product flags in the live DB; the factory has no
 * DB to read from, so the static markup always renders both even when the
 * product is neither new nor on sale. Stakeholder ask 2026-05-26: don't
 * show them on factory PDPs at all. `!important` because a later rule in
 * this same file (defined alongside the badge-item visual treatment) sets
 * `display: flex` to position the badges; we want the hide to win. */
.cf-landing.cf-marketing .badges {
  display: none !important;
}

/* Editorial text frames don't need the TYPO3-flavored top padding —
 * stakeholder ask 2026-05-26. Affects the wrap that lives below the
 * image+text pairs (FAQ + closing-thought sections). */
.cf-landing.cf-marketing .frame.frame-type-text {
  padding-top: 0;
}

/* PDP header polish — match live ORWO Aimeos catalog-detail visual weight. */
.cf-landing.cf-marketing .catalog-detail-basic .product-title {
  font-size: 2rem;
  font-weight: 700;
  display: block;
  margin: 0.5rem 0;
}

.cf-landing.cf-marketing .ts-rating-container {
  margin-bottom: 0.5rem;
}

/* .long-description ul — visible disc bullets, matching live ORWO. Live
 * relies on browser default list-style + Bootstrap's `ul { padding-left: 2rem }`
 * to surface bullet markers; without an explicit rule scoped to the PDP
 * column they end up clipped on our preview (col-md-6 + the parent's
 * left-padding can swallow the marker glyph). */
.cf-landing.cf-marketing .catalog-detail-basic .long-description ul {
  list-style: disc outside;
  padding-left: 1.25rem;
  margin: 0.5rem 0 1rem;
}

.cf-landing.cf-marketing .catalog-detail-basic .long-description li {
  margin-bottom: 0.25rem;
  line-height: 1.5;
}

.cf-landing.cf-marketing .more-description a {
  font-size: 0.9rem;
  text-decoration: underline;
}

/* `display: inline-flex` lets us control gaps explicitly without HTML
 * source-newlines bleeding through as whitespace (which `display: inline`
 * suffers from). We want NO gap inside the number ("15,95" not "15 , 95")
 * but small gaps around prefix/currency ("ab 15,95 €"). Default gap=0,
 * specific siblings get padding.
 *
 * No font-size override here — live ORWO's merged bundle declares
 * `.value { font-size: 2.6rem; font-family: "Futura PT Condensed Bold" }`
 * (= 41.6px) and we let that win for visual parity.
 */
/* Live measured values:
 *  .value parent: font-size 41.6px / line-height 41.6px / font-weight 600
 *  .value > span: font-size 32px / line-height 32px (smaller than parent)
 * We only set display + alignment here, no font-size/weight overrides — let
 * the merged ORWO bundle's `.value { font-size: 2.6rem }` (41.6px) and
 * span-level `.rm-price_int { font-size: 2rem }` (32px) win. */
.cf-landing.cf-marketing .articleitem.price.price-actual .value {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  /* Live's `.aimeos .product .price-item.default .value` rule (font-size:
   * 2.6rem) doesn't reach our DOM because we don't carry `.product` on an
   * ancestor. Re-declare the size + brand font here so the price reads at
   * the same visual weight as live (~41.6px Futura PT Condensed Bold). */
  font-size: 2.6rem;
  font-family: 'Futura PT Condensed Bold', Arial, Verdana, Helvetica;
  font-weight: 600;
  line-height: 1;
  color: var(--bs-dark, #212121);
}

/* Selective spacing — between "ab" and "15", and between "95" and "€". */
.cf-landing.cf-marketing .articleitem.price.price-actual .rm-price_prefix {
  padding-right: 0.3em;
}
.cf-landing.cf-marketing .articleitem.price.price-actual .rm-price_currency {
  padding-left: 0.2em;
}

/* Tax-asterisk after the currency — rendered semantically as <sup>*</sup>
 * inside .rm-price_suffix (matches live ORWO markup). Browser-native
 * superscript handles the visual lift correctly without CSS gymnastics.
 * No CSS rule needed. */

/* Live ORWO hides the taxrate next to the visible "ab 15,95 €" price on
 * desktop via `.price-item:first-of-type .taxrate { display: none }` (in a
 * media query). Match that — the tax-rate text adds visual noise next to the
 * big price and live doesn't show it. The frontmatter still carries the
 * value (used for footer/legal contexts), it's just visually suppressed here. */
.cf-landing.cf-marketing .articleitem.price.price-actual .taxrate {
  display: none;
}

/* The "Voraussichtliches Versanddatum" block (.catalog-delivery-info +
 * .delivery-info-*) was removed from the renderer per user request
 * 2026-06-04 — its rules went with it. */

/* Social-share row sits at the bottom of the right PDP column: the column is
 * a flex-column and the renderer puts Bootstrap `mt-auto` on this element, so
 * it tracks the bottom edge of the row. On md+ the gallery column carries a
 * 2rem bottom margin (see .catalog-detail-image below) which is what defines
 * the row bottom — mirror it here so the icons align flush with the bottom
 * edge of the gallery images, per user request 2026-06-04. padding-top keeps
 * the pre-existing 1rem minimum gap to the basket when the column is full. */
.cf-landing.cf-marketing .catalog-detail-actions {
  padding-top: 1rem;
  /* Mobile: breathing room between the share icons and the following
   * content section (per user request 2026-06-05); md+ overrides this to
   * align with the gallery's 2rem bottom margin. */
  margin-bottom: 1.5rem;
}

@media (min-width: 768px) {
  .cf-landing.cf-marketing .catalog-detail-actions {
    margin-bottom: 2rem;
  }

  /* SM-71 bottom-aligned the social-share row via Bootstrap `.mt-auto` (in the
   * flex-column right PDP column) so it lines up with the gallery's bottom edge.
   * A 2026-06-05 tweak neutralised it (icons under the CTA), but per user request
   * 2026-06-10 the icons must align to the BOTTOM of the teaser images again — so
   * mt-auto stays active. Combined with the matching 2rem bottom margin above and
   * the gallery's 2rem bottom margin, the icons sit flush with the gallery bottom.
   * Trade-off: a gap between the CTA and the icons when the gallery is taller than
   * the column content. (Mobile column isn't stretched, so mt-auto is a no-op there.) */
}

.cf-landing.cf-marketing .badges {
  position: absolute;
  z-index: 2;
  top: 0.5rem;
  left: 0.5rem;
  display: flex;
  gap: 0.35rem;
}

.cf-landing.cf-marketing .badge-item {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  color: #fff;
}

.cf-landing.cf-marketing .badge-item.new {
  background-color: #1c9b58;
}

.cf-landing.cf-marketing .badge-item.sale {
  background-color: #dc3545;
}

.cf-landing.cf-marketing .catalog-detail-image {
  position: relative;
}

/* Live page uses Slick carousel which sizes the hero. Without the slider
 * library running on our static preview, the <img> renders at its natural
 * dimensions which can look small inside the col-md-6 column. Force full
 * column width so the hero matches the live visual weight. */
.cf-landing.cf-marketing .catalog-detail-image .item.first,
.cf-landing.cf-marketing .catalog-detail-image > div > img {
  width: 100%;
  max-width: 100%;
  height: auto;
  display: block;
}

/* Thumbnail strip below the hero. Live ORWO renders the thumbs as 297.5×297.5px
 * each in a 619px wide container — basically two square cells side-by-side
 * with 12px margins (inner gap = 24px / 2). The whole strip is `d-none d-md-flex`
 * (hidden below 768px) — at mobile, the hero alone remains visible like a
 * single-image teaser, no thumbs.
 *
 * Each thumb dynamically shrinks with the column width: width = (100% - 24px) / 2
 * keeps the same 2-up layout from md breakpoint upward, scaling down with the
 * column instead of overflowing.
 */
/* Live measurement (1440px viewport): hero is 619×460 in col-md-6; thumbs are
 * 2 squares of 297.5×297.5 with a 24px gap, edges flush with hero left+right.
 * That's `flex: 1 1 0` (equal share) + `gap: 24px` — much simpler than the
 * old margin-arithmetic formula which left a 12px outer gutter on each side. */
.cf-landing.cf-marketing .catalog-detail-images-small {
  display: flex;
  flex-wrap: nowrap;
  gap: 24px;
  margin-top: 22.4px;
}

/* Live ORWO uses plain <div> children inside .catalog-detail-images-small
 * (no `-small-item` class). Updated 2026-05-06 to match. */
.cf-landing.cf-marketing .catalog-detail-images-small > div {
  flex: 1 1 0;
  /* Cap each thumb at half-row-minus-half-gap so a single thumb doesn't
   * stretch to fill the whole row and become as tall as the hero. With 2
   * thumbs the cap is exactly hit; with 1 thumb the right half stays empty
   * (matching live ORWO's small thumbnail size). Observed on
   * /detail/fotokacheln 2026-05-06 when extractor only captured 1 thumb. */
  max-width: calc(50% - 12px);
  aspect-ratio: 1 / 1;
  /* Reset stray 12px right-margin coming from a merged ORWO bundle's
   * generic `.aimeos div` rule — it adds 12px after each thumb and shrinks
   * each by ~12px, breaking the hero-edge alignment. */
  margin: 0 !important;
}

/* Live ORWO leaves a visible gap below the desktop hero+thumbnails block
 * before the catalog-detail-basic column visually settles. Without this
 * margin our gallery and the right-column basic info crowd the bottom of
 * the row. Per user request 2026-05-06. */
.cf-landing.cf-marketing .catalog-detail-image {
  margin-bottom: 2rem;
}

.cf-landing.cf-marketing .catalog-detail-images-small > div img,
.cf-landing.cf-marketing .catalog-detail-images-small > div video {
  width: 100%;
  height: 100%;
  border-radius: 2px;
  object-fit: cover;
  display: block;
}

/* ---------------------------------------------------------------------------
 * Mobile hero slider (PDP)
 *
 * Live ORWO ships a `.catalog-detail-image.catalog-detail-slider` block that
 * Slick turns into a 1-up swipeable carousel with a position indicator. The
 * factory reproduces the same per-slide markup (`.catalog-detail-images-slider`)
 * but does NOT load Slick/jQuery, so previously the slides stacked vertically.
 * This restores the slider with a dependency-free CSS scroll-snap track;
 * cf-slider.js injects the indicator bars and tracks the active slide. The
 * block is inherently mobile-only — the track is `d-block d-md-none`, so
 * desktop keeps its hero + thumbnail grid untouched. Added 2026-06-05.
 * --------------------------------------------------------------------------- */
.cf-landing.cf-marketing .catalog-detail-slider {
  flex-wrap: nowrap;
  align-items: flex-start;
  overflow-x: auto;
  /* Adaptive height: cf-slider.js sets an explicit track height matching the
   * active slide's media (like live ORWO's Slick adaptiveHeight), so the track
   * hugs each image/video instead of letterboxing in a fixed frame. Taller
   * slides scrolled off-screen must clip rather than stretch the track, hence
   * overflow-y hidden; the transition smooths the resize on settle. The 11 MB
   * autoplay-video lag that prompted the fixed-frame rework (QA 2026-06-05) is
   * separately fixed in cf-slider.js (preload="none" + play-only-when-active),
   * so adaptive height no longer brings it back (QA 2026-06-09). */
  overflow-y: hidden;
  transition: height 0.15s ease;
  /* Fallback height so the track never falls back to its flexbox natural height
   * (= the TALLEST sibling slide, e.g. the square or the unloaded-poster video),
   * which would dead-band the shorter hero before JS runs. 4/3 ≈ the universal
   * landing hero (every ORDE PDP's first image is ~1.25–1.34, median 1.34), so
   * the default already hugs it; cf-slider.js's fit() sets an explicit height
   * that overrides this per active slide. Guards the initial-load race where the
   * first fit() is delayed/missed (QA 2026-06-09).
   *
   * `width: 100%` is REQUIRED: aspect-ratio derives the missing dimension, so
   * with only an explicit height (what fit() sets) and an auto width it would
   * compute width = height × 4/3 — blowing the track wider than the viewport and
   * zooming/cropping the taller slides. A definite width makes aspect-ratio
   * apply only to the fallback height and be ignored once fit() sets a height. */
  width: 100%;
  aspect-ratio: 4 / 3;
  /* A flex container's automatic minimum size is its content (= the TALLEST
   * sibling slide), which would override the aspect-ratio fallback and let the
   * track grow to the square/video height → dead band under the hero. min-height:0
   * lets aspect-ratio (clipped by overflow-y:hidden) win. Required: without it,
   * real Chrome keeps the tallest-slide height on first load even though overflow
   * is hidden. */
  min-height: 0;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* Firefox */
  /* Spacing below the gallery is carried by .cf-slider-dots instead, so the
   * indicator bars sit close under the image like live ORWO. */
  margin-bottom: 0;
}
/* Show the track as a flex slider on MOBILE ONLY. This must be a max-width
 * media query, not just `!important`: the selector (3 classes) outweighs
 * Bootstrap's `.d-md-none` (1 class), so an unscoped `display: flex !important`
 * would beat `.d-md-none` and leak the slider onto desktop as a duplicate of
 * the hero. Scoped to <768px it still overrides `.d-block` on mobile but lets
 * `.d-md-none` hide the track on desktop. */
@media (max-width: 767.98px) {
  /* Full-bleed the slider edge-to-edge so the hero renders at ~natural width.
   * The col-md-6 (20px) + row (16px) + container (5px) padding chain otherwise
   * downscales the gallery to ~308px on a 390px screen. This is a VIEWPORT
   * BREAKOUT on the slider only — NOT a col-md-6 padding edit: that column is
   * shared with the product-info text, which must keep its padding. The dots
   * break out the same way so they stay centered under the full-width image. */
  .cf-landing.cf-marketing .catalog-detail-slider {
    display: flex !important;
    /* Full-bleed MINUS a Bootstrap half-gutter each side, so the gallery keeps a
     * small, CONSISTENT left/right space. We INSET the whole scrollport (reduce
     * width + center via margin) rather than pad it: `padding` on a scroll-snap
     * container only spaces the first/last slide and leaves the middle slides
     * flush to the edge (measured — slides 1–2 had 0 left gutter, QA 2026-06-09).
     * Insetting the scrollport keeps the gutter outside the scroll area, so every
     * slide is spaced equally. var(--bs-gutter-x) is the row's gutter (40px here);
     * half on each side. */
    width: calc(100vw - var(--bs-gutter-x, 1.5rem));
    margin-left: calc(50% - 50vw + var(--bs-gutter-x, 1.5rem) / 2);
    margin-right: calc(50% - 50vw + var(--bs-gutter-x, 1.5rem) / 2);
    /* Explicit default height = 4:3 of the inset width, so the track hugs the
     * ~4:3 hero on first paint. This is the RELIABLE cap: an explicit height
     * can't be overridden by the flex container's tallest child (the square/video
     * slide) — those just clip under overflow-y:hidden — whereas `aspect-ratio`
     * on a flex scroll-container did NOT cap in real Chrome and let the track grow
     * to the tallest slide → dead band under the hero (QA 2026-06-09). cf-slider.js's
     * fit() sets an exact per-slide px height that overrides this once it runs. */
    height: calc((100vw - var(--bs-gutter-x, 1.5rem)) * 0.75);
  }
  .cf-landing.cf-marketing .cf-slider-dots {
    width: 100vw;
    max-width: none;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
  }
}
.cf-landing.cf-marketing .catalog-detail-slider::-webkit-scrollbar {
  display: none; /* Chrome / Safari */
}

.cf-landing.cf-marketing .catalog-detail-slider .catalog-detail-images-slider {
  flex: 0 0 100%;
  width: 100%;
  max-width: 100%;
  /* Each slide fills the (fit-sized) track height so a <video> child can use
   * height:100% — see below. cf-slider.js sizes the track to the active slide's
   * media, so for the active slide this equals its natural height anyway (no
   * letterbox); off-screen slides clip under overflow-y:hidden. `start` rests
   * more pixel-stably at the clamped first/last slides than `center`; `always`
   * keeps swipes to one slide at a time. */
  height: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.cf-landing.cf-marketing .catalog-detail-slider .catalog-detail-images-slider > div {
  width: 100%;
  height: 100%;
}

/* Image: natural aspect, no crop. height:auto + the track being fit() to the
 * image's ratio means it fills the slide with no distortion. Do NOT give <img>
 * an explicit height — it has object-fit:fill by default and would distort. */
.cf-landing.cf-marketing .catalog-detail-slider .catalog-detail-images-slider img {
  width: 100%;
  height: auto;
  display: block;
}
/* Video: DEFINITE height (fills the slide). A preload="none" <video> has no
 * intrinsic size until metadata loads, so height:auto collapses it to a tiny
 * box on WebKit/iOS (the small poster in a tall track the customer saw). The
 * track is fit() to the video's poster/clip ratio, so height:100% + contain
 * fills the slide with the poster (and the playing frame) at the right aspect,
 * no distortion (QA 2026-06-09). */
.cf-landing.cf-marketing .catalog-detail-slider .catalog-detail-images-slider video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* Position indicator — pill bars injected by cf-slider.js, mirroring live
 * ORWO (dark = active slide, light = the rest). Bars flex-share a capped
 * width so 2 slides show wide bars and 8 slides still fit one mobile row. */
.cf-landing.cf-marketing .cf-slider-dots {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 8px;
  max-width: 280px;
  margin: 16px auto 2rem;
  padding: 0;
}
.cf-landing.cf-marketing .cf-slider-dots .cf-slider-dot {
  flex: 1 1 0;
  min-width: 0;
  max-width: 44px;
  height: 4px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: #d9d9d9;
  cursor: pointer;
  transition: background-color 0.2s ease;
  -webkit-appearance: none;
  appearance: none;
}
.cf-landing.cf-marketing .cf-slider-dots .cf-slider-dot.is-active {
  background: #111111;
}

/* Desktop keeps the hero + thumbnail grid; the slider track is hidden by its
 * `d-md-none` class, so hide the JS-injected indicator there too. */
@media (min-width: 768px) {
  .cf-landing.cf-marketing .cf-slider-dots {
    display: none;
  }
}

/* Social share buttons. Wrapper class `.catalog-social` matches live ORWO
 * (visibility audit checks for it), but the inner anchors use our custom
 * `.cf-share-link` class so live's catalog-detail.css `:before` icon-font
 * rules don't fire on them — those rules render a phantom checkmark glyph
 * on top of any SVG/img we put inside an anchor classed `social-button`.
 *
 * Right-aligned within the parent `.catalog-detail-actions` flex row via
 * `margin-left: auto`. */
.cf-landing.cf-marketing .catalog-social {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
  list-style: none;
  padding: 0;
  margin: 0 0 0 auto;
}

.cf-landing.cf-marketing .catalog-social li {
  display: inline-flex;
  margin: 0;
  padding: 0;
  line-height: 1;
  /* Live ORWO `.catalog-detail li` paints a checkmark-circle background-image
   * on every <li> in the catalog-detail tree. Without this override it shows
   * up behind every social-share icon. */
  background: none !important;
}

.cf-landing.cf-marketing .cf-share-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  width: 38px;
  height: 38px;
  padding: 0;
  line-height: 1;
  color: var(--bs-dark, #212121);
  transition: opacity 0.15s ease;
  font-family: inherit;
  font-size: inherit;
}

.cf-landing.cf-marketing .cf-share-link::before,
.cf-landing.cf-marketing .cf-share-link::after {
  content: none !important;
  display: none !important;
}

/* Suppress the shop's generic `<li>::before` checkmark rule (Aimeos-derived,
 * comes from orwo-bootstrap.css) on the social-share row. Without this the
 * Facebook/X/WhatsApp buttons each get a stray ✓ glyph next to them — see
 * the fotokacheln-test screenshot comparison vs live ORWO 2026-05-06. */
.cf-landing.cf-marketing .catalog-social > li::before,
.cf-landing.cf-marketing .catalog-social > li::after {
  content: none !important;
  display: none !important;
}

.cf-landing.cf-marketing .cf-share-link:hover {
  opacity: 0.7;
}

.cf-landing.cf-marketing .cf-share-link svg {
  width: 32px;
  height: 32px;
  display: block;
  fill: currentColor;
}

/* #ts-reviews placeholder section — collapsed to flush with the preceding
 * frame per user request 2026-05-13. Previous spacing (4rem margin-top,
 * 2rem padding-top, border-top divider) over-emphasised the reviews block;
 * the surrounding `.frame` wrapper already provides adequate vertical
 * rhythm. */
.cf-landing.cf-marketing #ts-reviews {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* Element-header inside the H1 frame (#cf-main-content) is upper-case to
 * mirror live ORWO's PDP H1 treatment. Scoped to the #cf-main-content id
 * so OTHER `.element-header` instances (Empfohlene Produkte, Häufige
 * Fragen, App-teaser heading) keep their original casing. */
.cf-landing.cf-marketing #cf-main-content .element-header {
  text-transform: uppercase;
}

/* Benefits strip below H1 — 3 cards with centered icon + title + description. */
.cf-landing.cf-marketing .container.content figure.image {
  margin: 0 auto 0.75rem;
  max-width: 96px;
}

/* FAQ accordion polish — match orwo-bootstrap.css visual weight. */
.cf-landing.cf-marketing .accordion-item {
  border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

.cf-landing.cf-marketing .accordion-button:not(.collapsed) {
  font-weight: 600;
}

.cf-landing.cf-marketing .accordion-body {
  padding-top: 0.75rem;
  padding-bottom: 1.25rem;
}

/* Recommended-products cross-sell — live ORWO uses Aimeos `.card-seen` /
 * `.list-column` markup with stock-status block + price + "Zum Produkt"
 * orwo-btn-secondary CTA. The merged ORWO bundle styles `.card-seen` and
 * `.price-item .taxrate` (display: none on first-of-type), so we only
 * supply small overrides:
 *   - taxrate hide for our card-seen offer block (live's :first-of-type
 *     rule already covers this; we add it explicitly so any cascade
 *     ordering quirks don't make it visible)
 *   - hover lift on card
 *   - button block-margin so it doesn't touch the price block
 */
.cf-landing.cf-marketing .card-seen .price-item .taxrate {
  display: none;
}

.cf-landing.cf-marketing .card-seen {
  transition: transform 0.15s ease;
}

.cf-landing.cf-marketing .card-seen:hover {
  transform: translateY(-2px);
}

.cf-landing.cf-marketing .card-seen .stocktext {
  font-size: 0.85rem;
  color: #198754;
}

.cf-landing.cf-marketing .card-seen .stocklevel {
  display: none;
}

.cf-landing.cf-marketing .card-seen .price-item .value {
  font-family: 'Futura PT Condensed Bold', Arial, Verdana, Helvetica;
  font-size: 1.6rem;
  font-weight: 600;
  display: block;
}

/* Bigger images in recommended cards — fill the column. */
.cf-landing.cf-marketing .card-seen .list-column img {
  width: 100%;
  height: auto;
  display: block;
}

/* Disabled direct-upload button — rendered for the 19 ORDE products in
 * pipeline/config/directUploadProducts.ts when priceBean.productPrice(...)
 * returns '#ERROR#' (missing format, unknown product). Matches both the
 * native :disabled pseudo-class and the [disabled] attribute selector so
 * dynamic JS toggles work the same as static markup. Greys out the button
 * and switches the cursor so users see at a glance that upload isn't
 * available for this combination. */
.cf-landing.cf-marketing .js-du-button:disabled,
.cf-landing.cf-marketing .js-du-button[disabled] {
  background: lightgray;
  color: gray;
  cursor: not-allowed;
  opacity: 0.7;
}

/* ---------------------------------------------------------------------------
 * cf-category-landing — content-after marketing block
 *
 * Lives below the product grid on /shop/{categoryPath} pages, renders the
 * orwo-highlight teasers + image+text rows + FAQ accordion. Scoped under
 * the cf-landing root so it doesn't leak. User-requested 2026-05-13:
 *   - bg-secondary + bg-primary teasers display side-by-side at 50% width
 *     (override the d-flex flex-wrap default that lets them grow to 100%)
 *   - H2 dividers (`<div class="row col-12 my-3">…</div>`) align LEFT, not
 *     centered — flex-wrap inheritance was nudging them to center
 * --------------------------------------------------------------------------- */
/* Scoped to md+ so the highlight teasers still stack full-width on mobile
 * (the underlying markup ships `col-12 col-md-6` — without this media
 * guard the `width: 50%` here would override `col-12` at xs/sm too and
 * the two cards would render side-by-side at 50% on phones). */
@media (min-width: 768px) {
  .cf-landing > section.aimeos.catalog-lists-content-after .orwo-highlight.bg-secondary,
  .cf-landing > section.aimeos.catalog-lists-content-after .orwo-highlight.bg-primary {
    flex: 0 0 auto;
    width: 50%;
  }
}

.cf-landing > section.aimeos.catalog-lists-content-after > .col-12.my-3 {
  text-align: left;
  justify-content: flex-start;
}
.cf-landing > section.aimeos.catalog-lists-content-after > .col-12.my-3 > .col-sm-12 {
  text-align: left;
}

/* ---------------------------------------------------------------------------
 * cf-category-landing — product card grid: force left alignment on the
 * trailing partial row.
 *
 * Symptom (2026-05-13, /shop/karten-selbst-gestalten, 5 cards): the second
 * row had its 2 cards visibly centered between columns 2–3 instead of
 * flowing into columns 1–2 like live ORWO does. Cause: somewhere in the
 * runtime CSS chain (likely Aimeos's `.catalog-list-items` reset) the flex
 * container ends up with `justify-content: center`, which `flex-wrap: wrap`
 * then propagates to the partial row. Forcing `flex-start` matches live
 * ORWO's behavior — orphan cards stay left-aligned. Scoped to
 * `.js-home-cards.row` so it only targets the factory-emitted grid, not
 * any other Aimeos catalog-list elsewhere in the page.
 * --------------------------------------------------------------------------- */
.cf-landing .catalog-list-items.js-home-cards.row {
  justify-content: flex-start;
}
