/* =============================================================================
   SPF PRODUCT CARD — die EINE Produktkarte (sitewide)
   -----------------------------------------------------------------------------
   Kili 16.07.: „bitte aus der Product Card in der Kategorie eine globale Klasse
   machen und die dort einfach einsetzen bzw. komplett neu einbauen ohne dass alte
   Regeln uebernommen werden. Ich moechte bei Aenderungen nicht ueberall doppelt
   rumdoktern."

   Gilt fuer Kategorie, Suche, Shop, Related/Upsells und Startseite. Markup kommt
   AUSSCHLIESSLICH aus SPF_Product_Card::render() — eine Quelle fuer Markup, eine
   fuer Optik. Loest `spf-product-cards.css` (Skin ueber die Bricks-`.ktmpc`) ab.

   TOKENS STEHEN AUF DER KARTE SELBST (`.spfcard`), NICHT auf einem Wrapper.
   -----------------------------------------------------------------------------
   Das ist der Kern des Umbaus, nicht Kosmetik: Das Karten-CSS lebte in
   `spf-category-page.css` und loeste `--spfcp-*` auf, die nur auf `.spfcp`
   (dem Kategorie-Wrapper) definiert sind. Ausserhalb der Kategorieseite — Suche,
   Shop, Startseite — gibt es diesen Wrapper nicht: Alle var() waeren undefiniert,
   die Karte transparent und ohne Rundung. Exakt der Fehler der „grauen Punkte"
   (Hub-Tokens nur auf Drawer/Fitbar gescopt, Tags auf den Karten unsichtbar).
   Eine sitewide Karte MUSS ihre Tokens mitbringen. Ueberschreiben geht weiter
   ueber die Kaskade (z. B. `.spfcp .spfcard{--spfcard-…}`), ohne !important.

   Namespace spfcard-*, px statt rem (62,5%-Root-Falle), kein !important.
   Night-Mode: body.spf-night (Header-Toggle, localStorage spflh_theme).
   Fitment-Tags (.spf-fit-flag/.spf-unfit-tag) rendert der Garage-Hub — hier nur
   Positionierung + Dim-Verhalten der unpassenden Karte.
   ============================================================================= */

.spfcard{
  /* Farbe */
  /* Signaturfarbe der Karte. Hiess und heisst „orange", zeigt aber auf die
     Markenfarbe: auf Husky war der Hover sonst KTM-Orange (Kili, 11.08.2026).
     Der Name bleibt, weil er an rund 20 Stellen in dieser Datei steht — ein
     Umbenennen waere eine eigene Runde mit eigener Pruefung. */
  --spfcard-orange:var(--brand-accent,#ff6600); --spfcard-orange-press:var(--brand-accent-dark,#e25c00);
  --spfcard-ink:#16181c; --spfcard-muted:#5b6270; --spfcard-faint:#666d78;
  --spfcard-bg:#ffffff; --spfcard-img-bg:#ffffff;
  --spfcard-line:#ececec; --spfcard-line-strong:#e0e1e3;
  /* Semantik. `bad` traegt zwei Rollen: Flaeche des Sale-Badges (weisse Schrift
     darauf) und Farbe des reduzierten Preises. Bei #e0453c waren beide bei
     4,13:1 — knapp unter WCAG. #cc372e hebt sie auf 5,06:1, bleibt aber
     eindeutig dasselbe Rot. */
  --spfcard-ok:#22a447; --spfcard-bad:#cc372e;
  /* Dasselbe Rot in der Schriftrolle. Im Hellmodus ist es identisch mit der
     Flaeche (5,06:1 auf Weiss); im Nachtmodus trennen sich die beiden, weil
     dort dieselbe Farbe als Schrift nur noch 3,38:1 schafft. */
  --spfcard-bad-text:var(--spfcard-bad);
  /* Form */
  --spfcard-r:14px; --spfcard-r-s:9px;
  --spfcard-shadow:0 1px 2px rgba(20,24,28,.04),0 8px 28px rgba(20,24,28,.06);
  --spfcard-shadow-hover:0 4px 8px rgba(20,24,28,.06),0 14px 36px rgba(20,24,28,.12);
  /* Schrift — Seiten-CI. Name/Preis = Blender Heavy, Rest Roboto Condensed
     (Kili 16.07.: „Schriftarten … fand ich im alten besser"). Beide Familien
     laedt bereits spf-garage-hub.css. */
  /* Auf Husky darf Blender gar nicht vorkommen (CI §2) — deshalb steht die schwere
     KTM-Hausschrift NICHT vor dem Marken-Token, sondern in dessen Fallback. */
  --spfcard-head:var(--spf-brand-head, 'Blender Heavy','Blender Pro','Oswald',system-ui,sans-serif);
  --spfcard-cond:var(--spf-brand-cond, 'Blender Pro','Oswald',system-ui,sans-serif);
  --spfcard-sans:var(--spf-brand-sans, 'Roboto Condensed Regular','Roboto Condensed',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif);
  /* Preisgroesse kommt aus der Markenpalette: Blender Heavy (KTM) und der
     Helvetica-Stack (Husky) wirken bei gleicher Punktzahl verschieden gross —
     entscheidend ist die x-Hoehe. KTM 22 px, Husky 21 (Kili, 13.08.2026). */
  --spfcard-preis:var(--spf-brand-preis-px, 22px);

  /* Flex-Spalte: Der Warenkorb-Button haengt sonst am Textfluss und rutscht bei
     kurzen Produktnamen hoch (gemessen: Button-Oberkanten 785/785/805/786 bei
     identischer Kartenhoehe 474). Mit flex + `margin-top:auto` unten sitzen sie
     garantiert auf einer Linie. (Kili 16.07.) */
  position:relative;display:flex;flex-direction:column;height:100%;
  background:var(--spfcard-bg);
  /* Ruhelinie ist wieder ein echter `border` — und `overflow` steht auf `visible`.
     Beides gehoert zusammen und loest denselben Knoten:

     Die Hover-Linie ist ein Pseudo-Element. Mit `inset:0` sitzt sie auf der
     PADDING-Box, also einen Pixel innerhalb des Randes; aussen blieb ein heller
     Saum. Der naheliegende `inset:-1px` legt sie genau auf die Border-Box — nur
     schnitt das `overflow:hidden` der Karte ihn wieder ab. Der Umweg darueber
     (Rand mitfaerben, spaeter: Rand weg, Ruhelinie als Innenschatten) hat beide
     Male etwas Neues kaputtgemacht: erst zwei Striche uebereinander, dann eine
     Ruhelinie, die am Bild verschwand — ein Innenschatten wird von jedem Kind
     mit eigenem Hintergrund ueberdeckt, und `.spfcard__imgwrap` hat einen
     (Kili, 12.08.2026: „Rahmen oben verloren gegangen bzw. jetzt uneinheitlich").

     Jetzt clippt nicht mehr die Karte, sondern das Bild sich selbst
     (`.spfcard__imgwrap` hat ohnehin `overflow:hidden`, es fehlte nur die
     Rundung oben). Damit darf die Hover-Linie ueber den Rand hinausragen und
     liegt exakt auf der Aussenkante. */
  border:1px solid var(--spfcard-line-strong);
  border-radius:var(--spfcard-r);
  overflow:visible;
  box-shadow:var(--spfcard-shadow);
  color:var(--spfcard-ink);
  font-family:var(--spfcard-sans);
  /* Grundgroesse gehoert der Karte, nicht ihrem Platz. Jedes Textteil setzt zwar
     seine eigene Groesse, aber geerbt hat die Karte bisher, was drumherum galt:
     15 px auf Kategorie- und Startseite, 17 px in der Produktseite
     (`.ktmpdp{font-size:17px}`). Sichtbar war das nie — es haette nur beim
     naechsten Teil ohne eigene Angabe zugeschlagen. Aufgefallen beim Vergleich
     der Karten am 12.08.2026. */
  font-size:15px;
  transition:transform .18s ease,box-shadow .18s ease;
}
.spfcard *,.spfcard *::before,.spfcard *::after{box-sizing:border-box}

body.spf-night .spfcard{
  /* Im Dunkelmodus traegt Huskys Dunkelblau nicht: auf #1a1c1e kommt es auf
     1,7:1. Dort ist Gelb die CI-Signatur (--spf-brand-night), bei KTM bleibt es
     dasselbe Orange. Genau dafuer hat die Palette zwei Ebenen. */
  --spfcard-orange:var(--spf-brand-night,var(--brand-accent,#ff6600));
  --spfcard-orange-press:var(--spf-brand-glow,var(--spfcard-orange));
  --spfcard-ink:#e8eaed; --spfcard-muted:#9aa0a6; --spfcard-faint:#868d96;
  --spfcard-bg:#1a1c1e;
  /* Bildzone bleibt WEISS: Produktfreisteller sind auf Weiss fotografiert —
     auf dunklem Grund stehen sie in einem hellen Kasten. Bewusste Ausnahme
     (identisch zur Kategorieseite). */
  --spfcard-line:#2a2d30; --spfcard-line-strong:#2a2d30;
  /* Der reduzierte Preis wird aufgehellt, das Sale-Badge NICHT.
     `--spfcard-bad` traegt zwei Rollen: Flaeche (weisse Schrift darauf, 5,06:1)
     und Schrift des Sale-Preises. Auf der dunklen Karte #1a1c1e schafft dieses
     Rot als Schrift nur 3,38:1 — bei 19 px sind 4,5 gefordert. Hellt man die
     Flaeche mit auf, faellt die weisse Schrift des Badges dafuer unter die
     Schwelle. Deshalb hier nur die Textrolle: #d85b54, 4,53:1, im HSL-Raum
     aufgehellt und damit erkennbar dasselbe Rot (Farbton und Saettigung
     unveraendert). Gefunden am 18.08.2026 beim Nachtmodus-Scan. */
  --spfcard-bad-text:#d85b54;
  --spfcard-shadow:0 1px 2px rgba(0,0,0,.3),0 8px 28px rgba(0,0,0,.35);
  --spfcard-shadow-hover:0 4px 8px rgba(0,0,0,.35),0 14px 36px rgba(0,0,0,.5);
}

@media (hover:hover){
  .spfcard:hover{transform:translateY(-3px);box-shadow:var(--spfcard-shadow-hover)}
}

/* ---------------------------------------------------------------- Teile ---- */
/* `tabular-nums`: Das Badge traegt jetzt den Rabatt („-25 %"). Proportionale Ziffern
   liessen die Zahlen ueber die Karten hinweg unterschiedlich breit wirken. */
/* `right:0` statt `-1px`: Der Ueberstand war dafuer gedacht, vom `overflow:hidden`
   der Karte buendig abgeschnitten zu werden. Die Karte clippt nicht mehr. */
.spfcard__sale{position:absolute;top:12px;right:0;z-index:3;background:var(--spfcard-bad);color:#fff;font-family:var(--spfcard-cond);font-weight:700;text-transform:uppercase;letter-spacing:.06em;font-size:11px;font-variant-numeric:tabular-nums;padding:4px 12px 4px 14px;clip-path:polygon(8px 0,100% 0,100% 100%,0 100%);pointer-events:none}
/* OEM-Badge: rechts oben am Bild, Stil analog zum PASST-Flag (Garage-Hub), aber
   dunkle „Original"-Pille statt Signalfarbe. PASST sitzt links oben -> keine
   Kollision damit; das Sale-Ribbon sitzt ebenfalls rechts oben -> per :has() nach unten. */
.spfcard__oem{position:absolute;top:8px;right:8px;z-index:3;display:inline-flex;align-items:center;background:var(--spfcard-oem,#20242b);color:#fff;font-family:var(--spfcard-cond);font-size:10.5px;font-weight:700;line-height:1.2;text-transform:uppercase;letter-spacing:.05em;padding:3px 8px;border-radius:6px!important;box-shadow:0 1px 3px rgba(0,0,0,.28);pointer-events:none}
.spfcard:has(.spfcard__sale) .spfcard__oem{top:40px}
body.spf-night .spfcard__oem{background:var(--spfcard-oem-night,#e8eaed);color:#16181c;box-shadow:0 1px 3px rgba(0,0,0,.5)}
body.spf-night .spfcard__oem svg{stroke:#16181c}
/* Stretched Link: Der <a> umschliesst nur den Namen, das ::after-Overlay macht die
   GANZE Karte klickbar. Grund siehe class-spf-product-card.php — der Germanized-
   Zusatz enthaelt einen eigenen Link, und <a> im <a> ist ungueltiges HTML.
   Ebenen: 1 = Klick-Overlay, 2 = echte Bedienelemente darueber, 3 = Hover-Rahmen
   (pointer-events:none, stoert also nichts). */
.spfcard__link{color:inherit;text-decoration:none}
.spfcard__link::after{content:"";position:absolute;inset:0;z-index:1}
.spfcard__link:focus-visible{outline:none}
.spfcard__link:focus-visible::after{outline:2px solid var(--spfcard-orange);outline-offset:-3px;border-radius:inherit}
/* Ist jetzt ein <a> (Bild fuehrt zum Produkt). `position:relative` + `z-index:2`
   hebt ihn ueber das Klick-Overlay des Namens-Links (z-index 1) — ohne das laege
   das Overlay darueber und der Bild-Link waere wieder nur so gut wie dieses.
   `color:inherit` und kein Unterstrich, damit der Link optisch nichts aendert. */
/* Die obere Rundung liegt seit dem 12.08.2026 hier statt an der Karte: Die Karte
   steht auf `overflow:visible`, damit die Hover-Linie bis auf die Aussenkante darf.
   Ein Pixel weniger Radius als die Karte, weil der Bildbereich in der Padding-Box
   sitzt — also innerhalb des 1px-Rands. */
.spfcard__imgwrap{position:relative;z-index:2;display:block;aspect-ratio:1/1;background:var(--spfcard-img-bg);border-bottom:1px solid var(--spfcard-line);border-radius:calc(var(--spfcard-r) - 1px) calc(var(--spfcard-r) - 1px) 0 0;overflow:hidden;color:inherit;text-decoration:none}
.spfcard__imgwrap img{display:block;width:100%;height:100%;object-fit:contain}
/* Bildhinweis („Symbolbild"). Leichte Auflage unten links im Bildfeld.
   `.spfcard__imgwrap` ist bereits `position:relative`, es braucht also keinen
   neuen Kontext. `pointer-events:none`, damit der Bildlink darunter klickbar
   bleibt. Bewusst KEIN `backdrop-filter`: Der kostet auf jeder Karte
   GPU-Arbeit, und lesbar ist der Hinweis auch ohne. */
.spfcard__bildhinweis{
	position:absolute;left:8px;bottom:8px;z-index:3;pointer-events:none;
	max-width:calc(100% - 16px);padding:3px 7px;border-radius:5px;
	font-family:var(--cond,inherit);font-size:10px;font-weight:600;line-height:1.3;
	letter-spacing:.05em;text-transform:uppercase;white-space:nowrap;
	overflow:hidden;text-overflow:ellipsis;
	color:#fff;
	/* Deckkraft 0,62 ist kein Zufall: Produktbilder haben fast immer weissen
	   Grund. Bei 0,38 stand Weiss auf hellgrau — 2,41:1, unlesbar. 0,62 ergibt
	   4,99:1 auf Weiss und 19,2:1 auf Schwarz und laesst das Bild trotzdem
	   sichtbar durchscheinen (gerechnet am 25.08.2026). */
	background:rgba(20,24,28,.62)
}
/* Name+SKU bleiben oben, Preis+Lieferzeit werden per `margin-top:auto` nach UNTEN
   an den Warenkorb-Button gedrueckt. Sonst rutschen sie bei kurzen Produktnamen
   mit hoch. (Kili 16.07.) */
/* KEIN position:relative hier — sonst wird der Body zum Bezugsrahmen des
   Klick-Overlays (`.spfcard__link::after{inset:0}`) und die Karte waere nur im
   Textbereich klickbar, nicht auf dem Bild. Bezug muss `.spfcard` bleiben. */
.spfcard__body{display:flex;flex-direction:column;flex:1 1 auto;padding:13px 15px 6px}
/* Deutsche Zusammensetzungen sind laenger als die Karte breit ist
   („Geraeuschreduzierungseinsatz", 27 Zeichen). Ohne Trennung schneidet die
   Karte sie am Rand ab — `overflow:hidden` liegt auf `.spfcard`. `hyphens:auto`
   trennt nach Sprachregel, `overflow-wrap` faengt den Rest ab, wo der Browser
   keine Trennstelle kennt. */
/* VERSALIEN, weil hier Blender Heavy laeuft und Blender eine Versalschrift ist
   (Kili, 14.08.2026: „Blender bitte immer in CAPS"). Vor dem Umstellen an den
   zehn Karten der KTM-Startseite geprueft: Auch der laengste Name („QUAD LOCK
   PONCHO / SCHUTZHUELLE FUER APPLE IPHONE", 52 Zeichen ueber drei Zeilen)
   bleibt lesbar; `hyphens` und `overflow-wrap` bleiben unveraendert.
   Auf Husqvarna laeuft der Helvetica-Stack — dort bleibt es gemischt, siehe
   die Ausnahme darunter. */
.spfcard__name{display:block;font-family:var(--spfcard-head);font-size:16px;font-weight:400;line-height:1.3;color:var(--spfcard-ink);hyphens:auto;overflow-wrap:break-word;text-transform:uppercase}
.spfcard--husky .spfcard__name{text-transform:none}
/* SKU ueber dem Namen, linksbuendig (Kili, 12.08.2026). Der Karten-Body ist
   eine Flex-Spalte, deshalb reicht `order` — das Markup bleibt, wie es ist, und
   die Vorlesereihenfolge auch: Wer die Karte hoert, bekommt zuerst den Namen. */
.spfcard__sku{display:block;order:-1;margin:0 0 2px;font-family:var(--spfcard-sans);font-size:13px;color:var(--spfcard-faint)}
/* Preiszeile: Preis + kleiner Pflicht-Zusatz nebeneinander (Kili 17.07.: „neben den
   Preis noch ganz klein den Germanized Hook mit mwst und versandkosten").
   `margin-top:auto` sitzt jetzt auf der ZEILE, nicht mehr am Preis — sonst wuerde
   nur der Preis nach unten gedrueckt und der Zusatz bliebe oben haengen.
   `align-items:baseline` laesst den 10px-Zusatz auf der Grundlinie des 17px-Preises
   sitzen; `flex-wrap` gibt ihm in schmalen Karten eine eigene Zeile, statt den
   Preis zu quetschen. */
.spfcard__pricerow{display:flex;align-items:baseline;flex-wrap:wrap;gap:2px 6px;margin-top:auto;padding-top:10px}
/* `white-space:nowrap`: Preis-Ranges variabler Produkte („336,10 € – 399,96 €") und
   „UVP: 11,96 € 9,57 €" sollen in EINER Zeile stehen und nicht am Gedankenstrich
   umbrechen. Stand vorher im Snippet 278084 und ist mit dessen Bereinigung hierher
   gewandert. */
.spfcard__price{flex:none;display:block;font-family:var(--spfcard-head);font-size:var(--preis-basis, var(--spfcard-preis,22px));font-weight:400;white-space:nowrap;color:var(--spfcard-ink)}
/* Der Zusatz kommt von Germanized (`p.wc-gzd-additional-info`) — Abstaende dort
   platt machen, sonst reisst der <p>-Default die Zeile auseinander. */
/* `display:inline` auf Germanizeds <p> -> mehrere Angaben (Grundpreis, MwSt,
   Versand) laufen als EIN Textfluss, statt je Angabe eine Zeile.
   KEIN white-space:nowrap: Die Karte hat `overflow:hidden` — ein zu langer Text
   wuerde abgeschnitten, und ein halber Pflichthinweis ist schlimmer als ein
   umgebrochener.
   12,5px statt der frueheren 9px (Kili 30.07., gleiche Groesse wie der Lieferstatus
   darunter). So gross passt der Hinweis nicht mehr neben einen 18px-Preis — er
   bekommt deshalb unten eine eigene volle Zeile
   (`flex-wrap` auf der Zeile + `flex-basis:100%` hier), statt neben dem Preis in
   vier Zeilen zu zerbroeseln.
   Die frueher konkurrierende Definition im WPCode-Snippet 278084
   (`flex:none !important; font-size:10px` + `flex-direction:column` auf der Zeile)
   ist am 30.07.2026 dort entfernt worden — die Karte gehoert vollstaendig diesem
   Stylesheet. */
.spfcard__pricenote{flex:1 0 100%;min-width:0;font-size:11px;line-height:1.35;color:var(--spfcard-faint)}
.spfcard__pricenote .wc-gzd-additional-info{display:inline;margin:0;padding:0;font-size:inherit;line-height:inherit;color:inherit}
/* Ueber dem Klick-Overlay, sonst waere der Versandkosten-Link tot. */
.spfcard__pricenote a{position:relative;z-index:2;color:inherit;text-decoration:underline;text-underline-offset:2px}
/* Durchgestrichener Preis auf der Groesse des aktuellen Preises (Kili 30.07.): Mit
   13px stand er deutlich unter „UVP:" und dem Verkaufspreis und wirkte wie eine
   Fussnote statt wie der Vergleichswert. Das UVP-Label (`.wc-gzd-sale-price-label`
   von Germanized) erbt die Groesse von `.spfcard__price` und laeuft mit. */
/* Der Streichpreis stand auf festen 18 px und war damit kleiner als der Preis
   daneben (Kili, 12.08.2026). Es ist derselbe Baustein — `font-size:inherit`
   haelt beide zusammen, auch wenn die Preisgroesse spaeter noch einmal wandert. */
.spfcard__price del{color:var(--spfcard-faint);font-weight:600;font-size:inherit;margin-right:6px}
.spfcard__price ins{text-decoration:none;color:var(--spfcard-bad-text)}
/* Lieferstatus und Preis-Suffix auf derselben Groesse (12,5px, Kili 30.07.) — sie
   stehen direkt untereinander und wirkten mit 11,5 vs. 13px unruhig. */
.spfcard__status{display:block;margin-top:3px;font-size:12.5px;font-weight:600}
.spfcard__status.is-in{color:var(--spfcard-ok)}
.spfcard__status.is-out{color:var(--spfcard-faint)}
.spfcard__actions{position:relative;z-index:2;margin-top:auto;padding:10px 15px 15px}
/* Warenkorb-Button: Woo-Standard (`woocommerce_template_loop_add_to_cart()`), weil
   er `data-product_id` (Hub-Fitment) und den AJAX-Warenkorb mitbringt.
   ------------------------------------------------------------------------------
   SEIN DESIGN GEHOERT BEWUSST NICHT UNS. Der Shop hat ein globales Woo-Button-Design
   im Bricks-Custom-CSS (`.woocommerce a.button, .add_to_cart_button, .checkout-button`
   u. a.): eckig, 2px Rahmen, oranger Grund, invertierter Hover (weiss/orange) — mit
   `!important` auf border-radius/border/background/color und sitewide gueltig
   (Produktseite, Warenkorb, Kasse). Kili 17.07.: Die Karte ordnet sich unter, damit
   alle Buttons im Shop gleich aussehen.
   Das ist auch die Aufloesung des Carryover-Punkts „Warenkorb-Button bleibt eckig"
   (v1.9.146, dort als offener Fehler gefuehrt): Es war nie eine Inline-Regel des
   Templates, sondern die CI — kein Bug. Live nachgewiesen: Inline-Style OHNE
   !important -> 0px, MIT !important -> 9px, hoehere Spezifitaet -> 0px.
   Deshalb steht hier KEIN radius/background/hover: Das waere totes CSS, das nur so
   aussieht, als taete es etwas. Wir setzen ausschliesslich die Geometrie in der Karte
   — Breite ueber `.spfcard .spfcard__actions a.button` (0,3,1 schlaegt deren 0,2,1
   OHNE !important, weil sie die Breite nicht setzen). */
.spfcard .spfcard__actions a.button,
.spfcard .spfcard__actions button.button{width:100%}
.spfcard__actions .added_to_cart{display:block;margin-top:7px;text-align:center;font-size:12px;color:var(--spfcard-muted)}
/* Germanized-Reste NEBEN der Karte: Im Woo-Loop rendert Germanized alles, was NICHT
   zum Preis gehoert (Lieferzeit, Einheiten), als `p.wc-gzd-additional-info` als
   Geschwister in `li.product` — so wie heute neben `.ktmpc`. Preis-Zusatz und
   Grundpreis holt sich die Karte selbst nach vorn (`.spfcard__pricenote`).
   Geschwister-Selektor statt `li.product`, damit die Regel nur greift, wo auch
   unsere Karte steht — Fremd-Loops bleiben unberuehrt. Tokens sind hier ausserhalb
   der Karte nicht definiert -> Fallback-Werte. */
.spfcard ~ .wc-gzd-additional-info{margin:7px 0 0;padding:0 15px;font-size:11px;line-height:1.4;color:var(--spfcard-faint,#9aa0a6);font-family:var(--spfcard-sans,system-ui,sans-serif)}
.spfcard ~ .wc-gzd-additional-info a{color:inherit;text-decoration:underline;text-underline-offset:2px}

/* ------------------------------------------------------------- Fitment ----- */
/* Tags rendert der Hub (.spf-fit-flag/.spf-unfit-tag), Klassen .spf-fit/.spf-unfit
   setzt er an der Karte. Unpassende Karte: NICHT die Huelle ausgrauen, sonst wird
   der Tag selbst unlesbar — nur Inhalt dimmen. */
.spfcard .spf-unfit-tag,.spfcard .spf-fit-flag{top:10px;left:10px}
.spfcard.spf-unfit{opacity:1;filter:none}
.spfcard.spf-unfit .spfcard__imgwrap,
.spfcard.spf-unfit .spfcard__body,
.spfcard.spf-unfit .spfcard__actions{opacity:.45;filter:grayscale(.65);transition:opacity .2s,filter .2s}
@media (hover:hover){
  .spfcard.spf-unfit:hover .spfcard__imgwrap,
  .spfcard.spf-unfit:hover .spfcard__body,
  .spfcard.spf-unfit:hover .spfcard__actions{opacity:.85;filter:grayscale(.2)}
}

/* ---------- Hover: zwei Linien laufen von unten hoch, treffen sich oben in der Mitte ----------
   Kili 17.07.: „die Linien von unten nach oben laufen, und sich oben in der Mitte wieder
   treffen, statt dass oben eine eigene Linie rauskommt."
   Loesung: KEIN separater Oberkanten-Balken mehr (der wuchs vorher aus der Mitte heraus =
   „eigene Linie"). Stattdessen tragen ::before und ::after je einen VOLLEN Rahmen (border
   rundum, border-radius:inherit) und werden per clip-path auf die LINKE bzw. RECHTE Haelfte
   beschnitten. Die obere Polygon-Kante faehrt beim Hover von unten (100 %) nach oben (0 %):
   der Rahmen enthuellt sich von unten, die Seiten wachsen hoch, und die Oberkante schliesst
   sich in der Mitte, wo beide Haelften bei x=50 % aufeinandertreffen.
   Reines clip-path-Reveal eines statischen Rahmens -> die Rundung wird nicht gestaucht
   (der alte scaleY/scaleX-Ansatz verzog sie an den Ecken). */
.spfcard::before,
.spfcard::after{
  content:"";position:absolute;inset:-1px;z-index:3;pointer-events:none;
  border:2px solid var(--spfcard-orange);
  /* `inset:-1px` schiebt das Element vom Padding- auf die Border-Box: Es deckt
     sich exakt mit der Aussenkante der Karte, der 1px-Rand liegt vollstaendig
     unter der 2px-Linie. Deshalb hier auch der volle Kartenradius und nicht
     `calc(var(--spfcard-r) - 1px)` wie frueher. */
  border-radius:var(--spfcard-r);
  transition:clip-path .4s cubic-bezier(.22,.61,.36,1);
}
/* Ruhe: alle Polygon-Punkte unten (Hoehe 0) -> nichts sichtbar. */
.spfcard::before{clip-path:polygon(0% 100%,50% 100%,50% 100%,0% 100%)}   /* linke Haelfte  */
.spfcard::after {clip-path:polygon(50% 100%,100% 100%,100% 100%,50% 100%)} /* rechte Haelfte */
/* Hover/Fokus: obere Kante faehrt hoch -> Haelfte voll, beide treffen sich bei x=50 %.
   Der Hover-Zweig steckt in `@media (hover:hover)`: Auf Touch simuliert der Browser
   beim Tap einen Hover, der danach an der Karte KLEBEN bleibt — der Rahmen stuende
   dann dauerhaft um die zuletzt angetippte Karte. Der Fokus-Zweig bleibt ausserhalb,
   damit Tastatur-Navigation ueberall funktioniert (auch am Tablet mit Tastatur). */
@media (hover:hover){
  .spfcard:hover::before{clip-path:polygon(0% 0%,50% 0%,50% 100%,0% 100%)}
  .spfcard:hover::after {clip-path:polygon(50% 0%,100% 0%,100% 100%,50% 100%)}
}
.spfcard:focus-within::before{clip-path:polygon(0% 0%,50% 0%,50% 100%,0% 100%)}
.spfcard:focus-within::after {clip-path:polygon(50% 0%,100% 0%,100% 100%,50% 100%)}

/* Die frueheren Regeln zum Mitfaerben des Kartenrands stehen hier nicht mehr.
   Sie waren der Versuch, den hellen Saum aussen um die Hover-Linie zu schliessen,
   solange diese eine Padding-Box weiter innen sass. Mit `border:0` an der Karte
   gibt es weder Saum noch zweiten Strich — und damit auch nichts zu verzoegern.
   Die Verzoegerung (`transition-delay: 0s,0s,.4s`) ist mit entfallen; sie war die
   Notloesung dafuer, dass der zweite Strich sonst schon beim Betreten dastand. */

/* ---------- Touch: Druckfeedback statt Hover ----------
   Auf Touch gibt es keinen Zeiger, der „darueber schwebt" — ein Hover-Zustand ist dort
   entweder wirkungslos oder bleibt nach dem Tap stehen. Stattdessen sinkt die Karte
   beim Antippen kurz ein: Rueckmeldung genau so lange, wie der Finger liegt.
   Der Tap auf den Warenkorb-Button ist ausgenommen — der hat sein eigenes Feedback,
   und die ganze Karte einsinken zu lassen lenkt davon ab. Faellt `:has()` aus, greift
   nur die Ausnahme nicht; das Druckfeedback selbst bleibt. */
@media (hover:none){
  .spfcard:active{transform:scale(.96)}
  .spfcard:has(.spfcard__actions :active){transform:none}
}

/* ------------------------------------------------------------ Responsive --- */
@media (max-width:767px){
  .spfcard__body{padding:11px 12px 4px}
  .spfcard__actions{padding:8px 12px 12px}
  /* min-height haelt die Preiszeile ueber zwei Karten hinweg auf einer Linie,
     auch wenn ein Name einzeilig und der daneben zweizeilig ist. */
  .spfcard__name{font-size:13px;min-height:35px}
  /* Preis bleibt auch mobil bei 18px (Kili 30.07.) — er ist die Information, auf die
     der Blick zuerst faellt, und wurde vorher auf 15px heruntergezogen. */
}

/* --------------------------------------------------------- Bewegungsarm ---- */
@media (prefers-reduced-motion:reduce){
  .spfcard,.spfcard::before,.spfcard::after{transition:none}
  .spfcard:active{transform:none;box-shadow:var(--spfcard-shadow)}
  @media (hover:hover){
  .spfcard:hover{transform:none;box-shadow:var(--spfcard-shadow)}}
}

/* ========================================================================
   Add-to-Cart: Button-Feedback + Toast (global — Produktseite UND Karten).
   Wird von assets/js/spf-cart-links.js gesteuert.
   ======================================================================== */
/* Normalfall: Das Feedback ist optimistisch, der Button springt sofort auf
   .spf-added und faellt bei einem Fehler auf .spf-addfail.

   AUSNAHME `.spf-wartet` (17.09.2026): Wo der Erfolg erst nach der Serverantwort
   feststeht — heute allein auf der Gutscheinseite, siehe `warten` in
   spf-cart-links.js — braucht es dazwischen ein Zeichen, das NICHTS behauptet.
   Deshalb weder gruen noch rot, sondern nur gedaempft und gesperrt. */
.single_add_to_cart_button.spf-wartet,
.add_to_cart_button.spf-wartet {
  opacity: .72;
  cursor: progress;
  pointer-events: none;
}
.single_add_to_cart_button.spf-added,
.add_to_cart_button.spf-added {
  background: #16a34a !important; border-color: #16a34a !important; color: #fff !important;
}
.single_add_to_cart_button.spf-addfail,
.add_to_cart_button.spf-addfail {
  background: #b91c1c !important; border-color: #b91c1c !important; color: #fff !important;
}

.spf-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%) translateY(150%);
  z-index: 100000;
  display: flex;
  align-items: center;
  gap: 12px;
  width: max-content;
  max-width: calc(100vw - 32px);
  padding: 13px 14px 13px 16px;
  background: #fff;
  border: 1px solid #e3e5e8;
  border-radius: 14px;
  box-shadow: 0 14px 44px rgba(0, 0, 0, .18);
  font-size: 15px;
  color: #16181c;
  opacity: 0;
  pointer-events: none;
  transition: transform .34s cubic-bezier(.22, .61, .36, 1), opacity .2s ease;
}
.spf-toast.is-open { transform: translateX(-50%) translateY(0); opacity: 1; pointer-events: auto; }
.spf-toast__ok {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: #16a34a;
  color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; line-height: 1;
}
.spf-toast--err .spf-toast__ok { background: #b91c1c; }
.spf-toast__msg { flex: 0 1 auto; line-height: 1.3; }
.spf-toast__view {
  flex: 0 0 auto;
  border: 0; cursor: pointer;
  background: var(--brand-accent, #ff6600); color: #fff;
  font-weight: 700; font-size: 14px;
  padding: 9px 15px;
  border-radius: 9px;
  transition: background .12s;
}
@media (hover:hover){
.spf-toast__view:hover{ background: var(--brand-accent-dark, #e65c00); }}
.spf-toast__x {
  flex: 0 0 auto;
  border: 0; background: transparent; cursor: pointer;
  color: #9aa0a6; font-size: 21px; line-height: 1;
  padding: 2px 4px;
}
@media (hover:hover){
.spf-toast__x:hover{ color: #16181c; }}

/* Night-Variante (body.spf-night <-> localStorage spflh_theme=dark) */
body.spf-night .spf-toast { background: #1f2226; border-color: #34383e; color: #e8eaed; box-shadow: 0 14px 44px rgba(0, 0, 0, .55); }
body.spf-night .spf-toast__x { color: #7a8089; }
@media (hover:hover){
body.spf-night .spf-toast__x:hover{ color: #e8eaed; }}

@media (max-width: 560px) {
  .spf-toast { left: 16px; right: 16px; width: auto; transform: translateY(150%); flex-wrap: wrap; }
  .spf-toast.is-open { transform: translateY(0); }
  .spf-toast__msg { flex: 1 1 100%; order: 1; }
  .spf-toast__ok { order: 0; }
  .spf-toast__view { order: 2; flex: 1 1 auto; }
  .spf-toast__x { order: 3; }
}


/* =============================================================================
   Schmale Bildschirme: Hinweis einmal am Seitenende statt in jeder Karte

   Der Steuer- und Versandhinweis ist in jeder Karte derselbe und zwei bis drei
   Zeilen lang. Auf dem Handy kostet das in einem zweispaltigen Raster mehr
   Platz als der Preis selbst. Deshalb steht dort ein Sternchen am Preis, und
   der Satz einmal unter der Seite (Kili, 12.08.2026).

   Der Grundpreis bleibt in der Karte: Er steht je Artikel anders und gehoert
   nach PAngV an die Ware. Ausgelagert wird nur der Teil, der ueberall gleich
   ist — `.spfcard__pn-allg`.
   ============================================================================= */

/* WIEDER NUR MOBIL (Kili, 04.09.2026)
   Am 03.09.2026 war sie auf alle Breiten gestellt worden, weil der Desktop
   angeblich gar keinen Steuer-/Versandhinweis hatte. Das trifft heute nicht mehr
   zu: Auf 1396 px nachgemessen traegt JEDE Karte den Hinweis selbst
   (`.spfcard__pn-allg`, „inkl. 19% USt., zzgl. Versandkosten") — die Fussnote im
   Seitenfuss stand also doppelt daneben.
   Mobil bleibt sie noetig: Dort ist `.spfcard__pn-allg` ausgeblendet (siehe
   Media Query unten), weil der Hinweis in einer 172-px-Karte keinen Platz hat.
   Genau dafuer wurde die Fussnote gebaut — der Preis steht in der Karte ohne
   Suffix, der Hinweis einmal zentral im Fuss.
   PAngV bleibt damit auf beiden Breiten erfuellt, nur eben nicht doppelt. */
.spf-preisfussnote{display:none}

@media (max-width:767px){
  .spfcard__pn-allg{display:none}
  /* 22 px passen in einer 172-px-Karte nicht mehr in eine Zeile, sobald ein UVP
     davorsteht („UVP: 24,99 € 19,99 €" braucht 192 px) — und `overflow:hidden`
     der Karte schnitt den Preis dann ab. Auf dem Handy darf die Zeile deshalb
     umbrechen; die Betraege selbst bleiben zusammen, damit nichts mitten in
     einer Zahl reisst. */
  /* Auf dem Handy soll der Preis NICHT umbrechen (Kili, 12.08.2026) — also muss
     er in die Karte passen. Der breiteste Fall ist ein Preis mit UVP
     („UVP: 54,44 € 43,55 €"): rund 181 px bei 18 px Schriftgroesse. Die Karte
     ist bei 390 px Fenster 172 px breit, bei 360 px nur 157 — eine feste Zahl
     passt also nicht fuer beide.
     Deshalb waechst der Preis mit dem Fenster: 4,3 vw ergibt 15,5 px bei 360 und
     16,8 px bei 390, beides unter der Grenze. Ab rund 512 px Fensterbreite sind
     die 22 px vom Desktop erreicht; dort sind die Karten laengst breit genug.
     Die Untergrenze von 13,5 px greift erst unter 315 px Fensterbreite — dort
     ist die Karte nur noch 139 px breit (iPhone SE der ersten Reihe).
     Nachgemessen ueber alle Karten der Startseite bei 320, 360, 390 und 430 px. */
  /* Seit dem 31.08.2026 ueber `--preis-basis` statt direkt: Die Laengenstufen
     am Dateiende deckeln diesen Wert, statt ihn zu ueberschreiben — sonst
     haetten sie hier die muehsam gemessene Handy-Groesse wieder aufgehoben. */
  .spfcard__price{--preis-basis:clamp(13.5px, 4.3vw, var(--spfcard-preis,22px));white-space:nowrap}
  /* Die Zeile mit UVP ist ein anderer Fall und braucht eine eigene Zahl.
     „UVP: 451,96€ 250,00€*" ist mit Sternchen 155 px breit bei 16 px Schrift —
     fast das Doppelte eines einfachen Preises. Mit den 4,3 vw von oben lief sie
     bei 360 px Fenster 23 px ueber den Kartenrand und wurde von `overflow:hidden`
     abgeschnitten (gemessen 12.08.2026, aufgefallen an einer Karte mit -45 %).
     Gemessen wurde, wieviel Platz die Karte im Rasterschritt wirklich hat, und
     daraus die groesste Schrift, bei der die Zeile noch hineingeht:

       320 px Fenster -> 110 px Platz -> bis 11,35 px = 3,55 vw   (der Engpass)
       360 px         -> 130 px       -> bis 13,41 px = 3,73 vw
       390 px         -> 145 px       -> bis 14,96 px = 3,84 vw
       430 px         -> 165 px       -> bis 17,02 px = 3,96 vw

     3,4 vw traf bei 320 px auf 0,1 px genau die Kante — die Rechnung oben
     unterschlaegt den `margin-right:6px` des Streichpreises. Mit 3,3 vw bleiben
     ueberall mindestens 2 px Luft; gegengeprueft, indem der breiteste denkbare
     Preis in eine echte Karte eingesetzt und nachgemessen wurde (320/360/390/430).
     Umbrechen ist keine Alternative: Der Preis soll auf dem Handy einzeilig
     bleiben (Kili, 12.08.2026). */
  .spfcard__price:has(del){--preis-basis:clamp(10px, 3.3vw, var(--spfcard-preis,22px))}
  /* Sternchen direkt hinter den Preis. `::after` am Preis und nicht als
     eigenes Element: So bleibt es an der Zahl kleben und wandert bei einem
     Preisbereich („12,50 € – 24,99 €") ans Ende.
     Kein Abstand: Das Zeichen bringt selbst genug Vorbreite mit, und durch
     `vertical-align:super` steht es ohnehin ueber der Grundlinie. Die 2 px, die
     hier standen, kamen zu einem Leerzeichen aus dem WooCommerce-Markup hinzu
     — zusammen sah das Sternchen aus, als gehoere es nicht mehr zum Preis
     (Kili, 12.08.2026). Das Leerzeichen ist in `price_html()` weg. */
  .spfcard__price::after{content:'*';margin-left:0;font-size:.7em;vertical-align:super;color:var(--spfcard-faint)}
  /* Leere Notizzeile einziehen, wenn ausser dem allgemeinen Teil nichts drin
     stand — sonst bliebe eine Luecke unter dem Preis. */
  .spfcard__pricenote:not(:has(.wc-gzd-additional-info)){display:none}

  /* Sie sitzt jetzt IN der Fussleiste (siehe SPF_Product_Card::fussnote) und
     braucht dort weder eigene Breite noch eigenen Aussenabstand — beides kommt
     vom Fuss. Nur Groesse und Farbe bleiben ihre Sache. */
  .spf-preisfussnote{
    display:block;
    margin:0;
    font-family:var(--spfcard-sans);
    font-size:11px;
    line-height:1.4;
    color:var(--spfcard-faint);
  }
  /* Ausserhalb des Fusses (Rueckfall-Haken) braucht sie ihn doch. */
  .spffo__in--bar > .spf-preisfussnote{ margin:0; }
  body > .spf-preisfussnote,
  main > .spf-preisfussnote{
    max-width:var(--site-max,1320px);
    margin:0 auto;
    padding:14px 16px 22px;
  }
  .spf-preisfussnote a{color:inherit;text-decoration:underline;text-underline-offset:2px}
}

/* ── Explosionszeichnung als Kartenbild ───────────────────────────────────
   Ersatzteile haben ueberwiegend kein Foto (91 von 98 Karten einer Suchseite,
   18.08.2026). Statt des grauen Platzhalters steht dort die Zeichnung der
   Baugruppe mit einem Ring auf der Position des Teils. Markup aus
   SPF_Part_Reference::karte_html().

   Der Rahmen MUSS die Bildproportion tragen: Der Ring ist ein eigenes Element
   mit Prozentkoordinaten. Laege das Bild mit `object-fit: contain` im
   quadratischen Kartenfeld, bezoegen sich die Prozente auf das Feld statt auf
   das Bild, und der Ring saesse daneben — derselbe Fehler wie auf der
   Produktseite am 13.08.2026. */
.spfcard__imgwrap--riss{display:flex;align-items:center;justify-content:center;padding:10px}
/* `width` statt `max-height`: Eine Hoehenbegrenzung staucht den Rahmen, ohne die
   Breite mitzunehmen — die Proportion bricht und der Ring wandert. Die
   Fallunterscheidung steckt im `min()`: Bei einer breiten Zeichnung (Regelfall)
   greift 100 %, bei einer hochformatigen der zweite Term, der die Hoehe auf
   100 % begrenzt. */
.spfloc--karte{position:relative;display:block;
     aspect-ratio:var(--spfloc-ar, 1);
     width:min(100%, calc(100% * var(--spfloc-ar, 1)));
     max-width:100%}
.spfcard__imgwrap--riss .spfloc__img{display:block;width:100%;height:100%;object-fit:contain}
/* Groesser als die 5 % der Produktseite: Auf 180 px Kartenbreite waeren das
   9 px — mit 2 px Rahmen mehr Rand als Ring. */
.spfloc--karte .spfloc__ring{position:absolute;width:9%;aspect-ratio:1/1;
     transform:translate(-50%,-50%);border:1.5px solid var(--spfcard-orange);
     border-radius:50% !important;pointer-events:none;
     box-shadow:0 0 0 1.5px rgba(255,255,255,.85)}
/* Normdarstellung fuellt das Feld ohne Ring — sie zeigt das Teil selbst. */
.spfloc--karte-norm{display:flex;align-items:center;justify-content:center;width:100%;height:100%}
.spfloc--karte-norm svg{width:100%;height:100%;max-height:100%}
/* Nachtmodus: dieselbe Umkehr wie auf der Produktseite. Die Zeichnungen liegen
   als helle Grafik vor; ohne sie leuchtet das Kartenfeld weiss. */
body.spf-night .spfloc--karte .spfloc__img,
html.spf-night .spfloc--karte .spfloc__img{filter:invert(.9)}
body.spf-night .spfcard__imgwrap--riss,
html.spf-night .spfcard__imgwrap--riss{background:#191919}
@media print{.spfloc--karte .spfloc__img{filter:none !important}}


/* -------------------------------------------------- Ueberlange Preiszeilen ---
   „wenn ein artikel rabattiert ist, der 5 zahlen hat, also z.B. 220,50 €, dann
   passt das nicht mehr in die Zeile rein und steht ueber die Card drueber"
   (Kili, 31.08.2026).

   NACHGEMESSEN: Bei 900 px Fensterbreite ist die Karte im Kategorieraster
   195 px breit, die Zeile „UVP: 286,20 € 229,00 €" bei 22 px aber 217 px — sie
   stand 45 px ueber die Karte hinaus, bei fuenf von zehn Karten. Unter 768 px
   greifen die Handy-Regeln weiter oben, ab etwa 1000 px werden die Karten breit
   genug: Das Loch lag genau dazwischen.

   ZWEI GROESSEN ENTSCHEIDEN, und keine Sprache kennt beide. Wie breit die Karte
   ist, weiss CSS ueber die Container-Abfrage (`100cqi`). Wie lang der Preis ist,
   weiss nur PHP — `SPF_Product_Card::preis_zeichen()` haengt die Zahl der
   sichtbaren Zeichen als `--preis-zeichen` ans Element. Hier steht die Rechnung.

   DER FAKTOR 2.0 IST GEMESSEN, nicht gewaehlt — und zwar auf BEIDEN SHOPS,
   weil sie verschiedene Schriften tragen:

   | Shop | Breite je Zeichen bei 22 px | rechnerisch moeglicher Faktor |
   |---|---|---|
   | KTM   |  9,874 px | 2,228 |
   | Husky | 10,585 px | 2,078 |

   Husqvarna laeuft auf „Helvetica Neue LT Pro" und setzt breiter. Mit dem
   zuerst gewaehlten 2.1 — an KTM gemessen — stand die Zeile dort 2 px ueber;
   ein Faktor, der nur den einen Shop kennt, ist ein Faktor, der die Haelfte
   der Faelle nicht traegt. 2.0 liegt unter beiden Werten und laesst Luft fuer
   die Streuung innerhalb einer Zeile: „286,20" traegt breitere Ziffern als
   „2,86".

   `100cqi` ist die INNENbreite: Container-Einheiten beziehen sich auf die
   content-box, das Innenmass von `.spfcard__body` ist also schon abgezogen
   (nachgemessen: Body 278 px, Padding 2 x 15, `100cqi` = 248).

   `min(basis, …)` statt `clamp`: Die Rechnung darf die Zeile nur KLEINER
   machen, nie groesser. `--preis-basis` traegt weiter oben auf dem Handy einen
   eigenen, gemessenen Wert; ein unbedingtes `font-size` haette ihn aufgehoben.
   Passt der Preis, kommt die Rechnung ueber die Basis hinaus und `min` nimmt
   die Basis — dann aendert sich nichts.

   DIE UNTERGRENZE VON 9 PX ist der Notfallboden und wird genau einmal
   erreicht: 320 px Fenster (iPhone SE der ersten Reihe, Karte 138 px) mit einem
   vierstelligen Rabattpreis wie „UVP: 3.218,77 € 2.575,02 €". Die Rechnung
   ergibt dort 9,05 px; mit den zuerst gesetzten 10 px stand die Zeile 6 px
   ueber. Neun Pixel sind klein — aber die Alternative waere ein Umbruch, und
   der ist ausgeschlossen: Der Preis soll auf dem Handy einzeilig bleiben
   (Kili, 12.08.2026). Bei 390 px sind es im selben Fall 11,9 px.

   `container-type` sitzt auf `.spfcard__body` und nicht auf `.spfcard`: Der Body
   enthaelt ausser zwei versteckten `screen-reader-text`-Spans nichts absolut
   Positioniertes. Auf der Karte selbst haengen die Ecken-Marken
   (`.spfcard__oem`, `.spfcard__sale`, `.spf-fit-flag`); sie sind zwar am
   `.spfcard__imgwrap` verankert, aber ein Container waere ein zweiter
   Bezugsrahmen mehr, den spaeter niemand erwartet. */
.spfcard__body{container-type:inline-size}
.spfcard__price{
	font-size:min(
		var(--preis-basis, var(--spfcard-preis,22px)),
		max(9px, calc(100cqi * 2.0 / var(--preis-zeichen, 10)))
	);
}

/* =============================================================================
   KARTENKNOPF: DER TEXT WIRD NICHT MEHR ABGESCHNITTEN
   =============================================================================
   Kili am 03.09.2026: „Husky mobil ist der Text auf den product cards
   abgeschnitten." Gemessen an einer 170 px breiten Karte (die Breite, die zwei
   Spalten auf einem 390-px-Fenster ergeben):

     Husky   „In den Warenkorb"  braucht 175 px, verfuegbar 138  → 37 px fehlen
     KTM     dasselbe            braucht 158 px, verfuegbar 138  → 20 px fehlen

   Es ist also KEIN Husky-Fehler, sondern einer der Karte — auf Husky faellt er
   nur eher auf, weil dort der Helvetica-Fallback laeuft und KTM die schmalere
   Blender Pro hat. Der Knopf trug `white-space:nowrap` bei `overflow:hidden`,
   und damit verschwand das Wortende lautlos.

   Zwei Massnahmen, beide ohne Media-Query und ohne Container-Abfrage — die
   Breite haengt an der KARTE, nicht am Fenster (im Karussell ist sie 238 px,
   im Raster auf dem Handy 170), und der Knopf liegt ausserhalb von
   `.spfcard__body`, also ausserhalb des vorhandenen Containers.

   1. `white-space:normal` als Sicherheitsnetz: lieber umbrechen als
      abschneiden. Ein halbes Wort auf einem Kaufknopf ist schlimmer als ein
      zweizeiliger Knopf.
   2. Innenabstand von 24 auf 8 px und Laufweite von 0,5 auf 0,2 px. Bei
      138 px Knopfbreite waren 48 px Abstand mehr als ein Drittel der Flaeche.

   3. `text-align:center`. Der Knopf stand auf `start`, also linksbuendig — was
      niemandem auffiel, solange der Text einzeilig war und das Ende ohnehin
      abgeschnitten wurde. Mit dem Umbruch fiel es sofort auf (Kili, 03.09.2026:
      „Auf Mobil sind beide zweizeilig und linksbündig, lass uns das vielleicht
      zentrieren").

   ZU DEN 8 px UND DER LAUFWEITE: Nachgemessen an der 230-px-Karte des
   Desktop-Rasters bricht „Ausführung wählen" bei 12 px Abstand und 0,5 px
   Laufweite auf zwei Zeilen (Kili: „Auf Desktop ist ausführung wählen jetzt
   zwei zeilen linksbündig, das sieht nicht gut aus"). Mit 8 px allein bleibt es
   zweizeilig, erst die kleinere Laufweite bringt es auf eine Zeile — der Text
   braucht dann rund 172 statt 177 px bei 182 px Platz. Auf dem Handy (170 px
   Karte) bleibt er zweizeilig; dort ist das unvermeidbar und jetzt wenigstens
   mittig.

   Der Puffer betraegt nur 10 px. Wird der Text laenger oder die Karte schmaler,
   bricht er wieder um — und das ist in Ordnung, weil zweizeilig und zentriert
   sauber aussieht. Was NICHT passieren darf, ist Abschneiden, und dagegen steht
   Punkt 1.

   `!important` ist hier noetig: Die Grundregel des Knopfes kommt nicht aus
   diesem Blatt (Theme beziehungsweise Snippet, per Textsuche nicht auffindbar),
   und sie setzt `nowrap` mit hoeherer Spezifitaet. */
.spfcard .add_to_cart_button,
.spfcard .button{
	white-space:normal !important;
	text-align:center !important;
	padding-left:8px !important;padding-right:8px !important;
	letter-spacing:.2px !important;
}

/* =============================================================================
   „zzgl. Versandkosten" IST EIN LINK — UND SIEHT JETZT AUCH SO AUS
   =============================================================================
   Kili am 03.09.2026: „zzgl. Versandkosten ist korrekt verlinkt, aber kein
   hover effekt, und somit für Kunden nicht verständlich."

   Der Link steckt im Germanized-Hinweis und erbt dessen Grauton — unterstrichen
   war er zwar, aber ohne jede Reaktion. Beim Zeigen wechselt er jetzt auf die
   volle Textfarbe und bekommt eine kraeftigere Unterstreichung.

   BEWUSST NICHT DIE MARKENFARBE: Orange auf Weiss erreicht bei 11 px keinen
   ausreichenden Kontrast, und auf Husky waere Orange ohnehin falsch. Der
   Wechsel von Grau auf die Textfarbe ist markenneutral und in beiden Modi
   kontraststark.

   In `@media (hover:hover)` gekapselt, damit die Regel auf dem Touchgeraet
   nicht am Finger kleben bleibt. Der Tastaturfokus bekommt dieselbe
   Hervorhebung plus sichtbaren Ring. */
.spfcard .wc-gzd-additional-info a,
.spfcard .woocommerce-price-suffix a,
.spfcard__pricenote a,
.spfcard ~ .wc-gzd-additional-info a{
	color:inherit;text-decoration:underline;text-underline-offset:2px;
	text-decoration-thickness:from-font;
	transition:color .12s ease,text-decoration-thickness .12s ease;
}
@media (hover:hover){
	.spfcard .wc-gzd-additional-info a:hover,
	.spfcard .woocommerce-price-suffix a:hover,
	.spfcard__pricenote a:hover,
	.spfcard ~ .wc-gzd-additional-info a:hover{
		color:var(--spfcard-ink);text-decoration-thickness:2px;
	}
}
.spfcard .wc-gzd-additional-info a:focus-visible,
.spfcard .woocommerce-price-suffix a:focus-visible,
.spfcard__pricenote a:focus-visible,
.spfcard ~ .wc-gzd-additional-info a:focus-visible{
	color:var(--spfcard-ink);text-decoration-thickness:2px;
	outline:2px solid var(--spfcard-orange);outline-offset:2px;border-radius:2px;
}
