/* ==========================================================================
   SCHAKAL SPORTS — Design-System / Basis

   Farbwelt:  Zwei Fassungen derselben Marke.
              HELL („Studio"): blasser Hallenboden, Papierweiß für Karten und
              Produktbühnen, Tiefschwarz für die Typo, Karminrot als Schnitt.
              DUNKEL („Halle bei Nacht"): warmes Anthrazit, Gunmetal, dasselbe
              Karminrot. Kein Signalrot, kein Neon.

              Hell ist der Standard. Dunkel greift, wenn der Betrachter es
              wählt (data-theme="dark") oder sein System dunkel steht und er
              nichts anderes gewählt hat.

   Typo:      Archivo (Display, variable Breite) · IBM Plex Sans (Text)
              IBM Plex Mono (technische Daten — OZ, Preise, Labels)
   ========================================================================== */

/* --- Tokens ------------------------------------------------------------- */
/* Die Namen sind semantisch, nicht farbig: --bg ist der Grund, --surface die
   aufliegende Fläche, --line die Haarlinie. Nur so kann derselbe Baustein in
   beiden Fassungen richtig liegen — in Hell liegt die Karte HELLER als der
   Grund, in Dunkel dunkler. Wer hier Farbnamen einsetzt („--anthrazit"),
   schreibt eine der beiden Fassungen fest. */
:root {
  /* --- Konstanten. Ändern sich in keiner Fassung ------------------------
     Sie tragen Text auf farbigen Flächen: Weiß auf Karminrot bleibt weiß,
     egal wie hell die Seite ringsum ist. */
  --night:      #16151a;   /* Tiefschwarz mit warm-violettem Bias */
  --bone:       #f2efe9;   /* Naturweiß der Handbandage */

  /* --- HELL (Standard) -------------------------------------------------- */
  /* Blasser Beton, nicht Creme: Der Grund kommt aus der Halle, nicht aus dem
     Papierladen. Die wärmere Fläche steht darüber als Karte. */
  --bg:         #e9e8e5;
  --surface:    #f6f5f3;
  --line:       #d6d4ce;
  --line-2:     #b9b6ae;

  --fg:         #17161a;
  --fg-mute:    #4d4b53;
  /* Die zurückhaltendste Textfarbe trägt die gesamte Mono-Label-Ebene: Größen
     in der Karte, Zahlarten, Versandzeile, Rechtszeile — und die
     Platzhalter-Marker, die auf ungeprüfte Angaben hinweisen sollen. Der
     Selbsttest misst sie in beiden Fassungen gegen --bg und --surface; unter
     4,5 fällt er durch. Ausgerechnet die Marker, die auffallen müssen, waren
     einmal am schwersten zu lesen. */
  --fg-faint:   #66636e;

  /* Die Gegenfarbe: Flächen und Kanten, die den Grund umkehren — der
     Füllwisch im Knopf, die Kante des gewählten Chips. In Hell ist das
     Schwarz, in Dunkel Weiß. Ohne dieses Paar bräuchte jede solche Regel
     zwei Fassungen. */
  --contrast:    var(--night);
  --on-contrast: var(--bone);

  /* Akzent — Karminrot, aus dem Neon des Gyms, entsättigt.
     --accent trägt Flächen (immer mit hellem Text darauf), --accent-br trägt
     TEXT und muss deshalb je Fassung die Seite wechseln: auf Hell tiefer als
     das Marken-Rot, auf Dunkel heller. */
  --oxblood:    #6b1a22;   /* tiefe Flächen, Zierlinien */
  --accent:     #b23a2b;   /* Buttonflächen, Marken-Rot */
  --accent-br:  #96271a;   /* Textakzent auf hellem Grund (Kontrast 6,6) */

  /* Gunmetal — aus dem grauen Handschuh */
  --steel:      #6e6f79;

  /* Semantisch. --on-ok trägt Text auf der grünen Fläche und muss deshalb
     mitwandern: Auf Hell ist das Grün tief genug für helle Schrift, auf
     Dunkel muss es selbst hell sein — dort kehrt sich die Schrift um. */
  --ok:         #3f6b3c;
  --on-ok:      var(--bone);
  --warn:       #7a5c0e;

  /* --- Flächen, Schleier, Schatten -------------------------------------
     Alles, was früher als hartcodiertes rgba() in den Bausteinen stand. Eine
     Fassung mehr hätte sonst geheißen, vierzig Stellen zu suchen. */
  /* Die Produktbühne: ein Lichtkegel wie auf dem Fototisch. In Hell ein
     weißer Pool auf blassem Grau, in Dunkel ein Schimmer auf Anthrazit. */
  --stage-flat: #e2e0db;
  --spot:       rgba(255, 255, 255, .92);
  --stage:      radial-gradient(ellipse 66% 58% at 50% 46%, var(--spot), transparent 72%), var(--stage-flat);
  /* Freisteller: Lichtkante nur auf dunklem Grund — auf Hell wäre sie ein
     weißer Saum auf Weiß. Der Faktor schaltet sie fassungsweise ab, ohne
     dass die Bausteine davon wissen müssen. */
  --rim-mul:    0;
  --pack-shadow: rgba(30, 27, 22, .26);
  /* Milchglas: Kopfzeile im Scrollen, feste Kaufleiste, Kaufleiste der Karte */
  --glass:      rgba(233, 232, 229, .82);
  --glass-2:    rgba(246, 245, 243, .95);
  /* Verdunkelung hinter Überlagerungen */
  --scrim:      rgba(35, 32, 28, .42);
  --scrim-voll: rgba(233, 232, 229, .96);
  /* Kante auf farbigen und bebilderten Flächen, wo --line zu schwach wäre */
  --edge:       rgba(23, 22, 26, .16);
  --edge-2:     rgba(23, 22, 26, .28);
  /* Der eine Lichtstoß, wenn der Handschuh im Auftakt landet */
  --treffer:    rgba(255, 255, 255, .95);
  /* Beide Logos sind SCHWARZ gezeichnet. Auf hellem Grund stimmt das; auf
     dunklem wären sie unsichtbar — deshalb kehrt die Fassung sie um, statt
     dass zwei Bilddateien gepflegt und im Markup getauscht werden müssten.
     Das Wasserzeichen im Auftakt braucht denselben Wechsel: Auf Hell
     dunkelt es die Fläche ab (multiply), auf Dunkel hellt es auf (screen). */
  --logo-filter: none;
  --mark-blend:  multiply;
  --mark-opacity: .038;
  /* Welcher Handschuh im Auftakt oben liegt. Auf dem hellen Grund der
     schwarze, im Anthrazit der weiße — jeweils der mit dem stärkeren
     Eigenkontrast. */
  --glove-hell:   1;
  --glove-dunkel: 0;
  /* Der Auftakt: Schleier über der Aufnahme. Hell holt das Foto an die
     Papierfläche heran, Dunkel senkt es ins Anthrazit. */
  --hero-veil:  linear-gradient(180deg, color-mix(in srgb, var(--bg) 86%, transparent) 0%, color-mix(in srgb, var(--bg) 30%, transparent) 34%, color-mix(in srgb, var(--bg) 12%, transparent) 62%, color-mix(in srgb, var(--bg) 55%, transparent) 100%);
  /* Höhe. In Hell weich und neutral, in Dunkel tief. */
  --shadow-s:   0 1px 2px rgba(30, 27, 22, .07), 0 4px 12px rgba(30, 27, 22, .06);
  --shadow-m:   0 2px 6px rgba(30, 27, 22, .08), 0 14px 34px rgba(30, 27, 22, .10);
  --shadow-l:   0 4px 12px rgba(30, 27, 22, .10), 0 26px 60px rgba(30, 27, 22, .16);

  /* Produktfarben (Farbwähler) */
  --p-grau:     #7c7e83;
  --p-rot:      #8e2b28;
  --p-weiss:    #ece9e4;
  --p-schwarz:  #1a1a1c;

  /* --- Rundung -----------------------------------------------------------
     Bis zum 16.8.2026 hatte diese Website genau drei border-radius: zwei
     Punkte und ein Kreis. Alles andere war rechtwinklig, und das war
     Absicht — Datenblatt, Werkstatt, kein Konsumentenglanz. Nur liest sich
     die Summe daraus nicht als Strenge, sondern als Härte: neunzehn Seiten
     aus lauter Rechtecken, die einander stumpf berühren.

     Die Rundung ist deshalb kein Zierat, sondern das, was Flächen
     voneinander löst. Sie steht als Leiter, nicht als eine Zahl: Je größer
     die Fläche, desto größer der Radius — sonst wirkt derselbe Wert an der
     Kachel weich und an der Bahn wie ein Fehler. Und weil sie in Tokens
     steht, ist die Stärke der ganzen Website eine Frage von fünf Zeilen. */
  --r-1:   .3rem;    /* Tupfer, Größenkästchen, Marker */
  --r-2:   .55rem;   /* Knöpfe, Chips, kleine Kacheln */
  --r-3:   .9rem;    /* Karten, Kästen, Leisten */
  --r-4:   1.4rem;   /* Große Flächen: Angebot, Überlagerung, Schublade */
  --r-5:   2.2rem;   /* Bahnen und Bühnen — alles über Bildschirmbreite */
  --r-voll: 999px;   /* Etiketten und Zähler, die als Pille lesen */

  /* Raster */
  --wrap:       1440px;
  --gutter:     clamp(1.25rem, 4vw, 3.5rem);
  --announce-h: 2.4rem;                     /* Höhe der Ankündigungsleiste */

  /* Abstände */
  --s-1: .25rem;  --s-2: .5rem;   --s-3: .75rem;  --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;
  --s-9: 6rem;    --s-10: 8rem;
  /* Sektionsabstände. Zwei aufeinanderfolgende Abschnitte summieren ihre
     Polster — bei den früheren 9 rem standen 288 px zwischen zwei
     Überschriften, die nichts trugen. Auf einer dunklen, kontrastarmen
     Fläche liest sich so viel Leere nicht als Ruhe, sondern als fehlender
     Inhalt. Jetzt 2 × 7 rem beziehungsweise 2 × 4 rem. */
  --sec-y:     clamp(4rem, 7.5vw, 7rem);
  --sec-tight: clamp(2.75rem, 5vw, 4rem);
  /* Dritte Stufe, seit dem 18.8.2026. Der Grund steht in components.css beim
     Rhythmus: Neunzehn Seiten aus gleich hohen Abschnitten lesen sich als
     Liste. Ein knapper Abschnitt zwischen zwei großen ist die Atempause, die
     den großen erst groß macht. */
  --sec-knapp: clamp(1.5rem, 2.6vw, 2.25rem);

  /* Bewegung */
  --ease:      cubic-bezier(.22, .61, .36, 1);
  --ease-out:  cubic-bezier(.16, 1, .3, 1);
  /* Landet einen Tick über dem Ziel und fällt zurück — der Unterschied
     zwischen „ist angekommen" und „ist eingeschlagen". Nur für den
     Auftakt; auf Bedienelementen wäre das Zappeln. */
  --ease-back: cubic-bezier(.18, 1.18, .32, 1);
  /* Fast sinusförmig, für die langsamen Dauerbewegungen im Auftakt */
  --ease-loop: cubic-bezier(.45, 0, .55, 1);
  /* Für lange Wege: setzt weich an UND kommt weich an. --ease-out reißt am
     Anfang los, was bei 200 ms richtig ist (eine Antwort soll sofort da
     sein) und bei 1,2 Sekunden falsch — dort sieht dasselbe Profil aus wie
     ein Ruck mit langem Nachlauf. Trägt die Ladesequenz und den Flug des
     Handschuhs. */
  --ease-flow: cubic-bezier(.62, .02, .18, 1);
  --t-fast:    .18s;
  --t-med:     .38s;
  --t-slow:    .75s;
  /* Die Einblendung beim Einscrollen hat einen eigenen Wert, weil sie als
     einzige gegen das Scrollen selbst läuft: Am Schreibtisch dreht ein
     Mausrad ein paar hundert Pixel, ein Wisch auf dem Handy dagegen eine
     ganze Seitenlänge. Mit 750 ms war ein Element auf dem Handy längst
     wieder aus dem Bild, bevor es fertig eingeblendet war — man sah nur
     noch, dass etwas halb da ist. Der Weg schrumpft mit: 14 px sind auf
     390 px Breite anteilig ein Vielfaches dessen, was sie auf 1440 px
     sind. */
  --t-reveal:  .75s;
  --reveal-weg: 14px;

  /* Schrift */
  --f-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --f-body:    'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --f-mono:    'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Die großen Preisangaben — Auftakt, Produktseite, Aufmacher, Warenkorb.
     Mono gibt jedem Zeichen dieselbe Breite, auch dem Komma: Bei 1 rem
     fällt das nicht auf, bei 2,6 rem steht links und rechts davon je ein
     halbes Zeichen Luft und „74,99 €" zerfällt sichtbar in zwei Blöcke.
     Deshalb Plex Sans mit tabellarischen Ziffern — proportionales Komma,
     trotzdem gleiche Ziffernbreite. Kleine Preise in Reihen (Datenblatt,
     Korbzeile, Paketliste) bleiben ausdrücklich Mono: Dort trägt die
     feste Breite die Ausrichtung, und genau dafür ist sie da. */
  --f-preis:   var(--f-body);

  color-scheme: light;
}

/* --- DUNKEL --------------------------------------------------------------
   **Hell ist seit dem 6.9.2026 der Standard, auch auf einem dunkel
   gestellten System.** Vorher entschied `prefers-color-scheme`, wenn der
   Besucher nichts gewählt hatte; ein Kunde mit dunklem Telefon bekam also
   die dunkle Fassung, ohne sie zu wollen. Der Betreiber hat das umgedreht:
   Der Laden soll hell aussehen, dunkel bleibt eine Wahl.

   Damit steht die Liste nur noch EINMAL, unter `[data-theme='dark']`. Die
   Doppelung samt `:root:not([data-theme='light'])` ist entfallen — sie war
   nur nötig, solange die Systemvorgabe mitreden durfte, und sie war die
   Stelle, an der die beiden Fassungen auseinanderlaufen konnten.

   Nur Werte, keine Regeln: Was sich hier nicht ändert, bleibt gemeinsam. */
:root[data-theme='dark'] {
  --bg:         #141317;
  --surface:    #1c1b21;
  --line:       #2a2830;
  --line-2:     #3b3843;

  --fg:         #ece9e3;
  --fg-mute:    #a1a0a9;
  --fg-faint:   #8b8a94;

  --contrast:    var(--bone);
  --on-contrast: var(--night);

  --accent-br:  #e4795e;
  --steel:      #8c8d96;
  --ok:         #6f9a6b;
  --on-ok:      var(--night);
  --warn:       #c8a24a;

  --stage-flat: #17161c;
  --spot:       rgba(237, 233, 227, .10);
  --rim-mul:    1;
  --pack-shadow: rgba(0, 0, 0, .55);
  --glass:      rgba(20, 19, 23, .86);
  --glass-2:    rgba(20, 19, 23, .94);
  --scrim:      rgba(8, 7, 10, .74);
  --scrim-voll: rgba(8, 7, 10, .95);
  --edge:       rgba(237, 233, 227, .14);
  --edge-2:     rgba(237, 233, 227, .3);
  --treffer:    rgba(237, 233, 227, .5);
  --shadow-s:   0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --shadow-m:   0 2px 8px rgba(0, 0, 0, .45), 0 16px 38px rgba(0, 0, 0, .4);
  --shadow-l:   0 6px 16px rgba(0, 0, 0, .5), 0 28px 66px rgba(0, 0, 0, .55);

  /* Diese fünf standen bis zum 18.8.2026 nur in der Fassung darüber, also
     nur für Systeme, die selbst dunkel melden. Wer ein helles System hat
     und hier die dunkle Fassung wählt, bekam sie nicht — und das ist der
     häufigere Weg zur dunklen Fassung, nicht der seltenere.

     Sichtbar war das an drei Stellen: Das Logo blieb schwarz auf dem
     Anthrazit und damit praktisch unsichtbar (genau der Fehler, der am
     10.8.2026 schon einmal behoben wurde), im Auftakt lag der schwarze
     Handschuh oben statt des weißen, und das Wasserzeichen mischte gegen
     den falschen Grund. */
  --logo-filter: invert(1) brightness(.96);
  --glove-hell:   0;
  --glove-dunkel: 1;
  --mark-blend:  screen;
  --mark-opacity: .055;

  color-scheme: dark;
}

/* --- Reset -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  background: var(--bg);
  color: var(--fg);
  /* Nur der Grund und die Schrift blenden über — die Bausteine darunter
     bringen ihre eigenen Übergänge mit und schalten deshalb im selben
     Moment mit. Ein pauschales `transition: all` auf allem wäre beim
     Umschalten eine halbe Sekunde Brei. */
  transition: background-color .35s var(--ease), color .35s var(--ease);
  font-family: var(--f-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* `clip`, nicht `hidden`. Der Unterschied ist keine Feinheit: overflow-x
     hidden macht aus dem <body> einen eigenen Scrollbereich, und darin
     klebt `position: sticky` an einer Fläche, die selbst nicht scrollt —
     also nie. Betroffen war damit jedes klebende Element der Website: der
     Kaufbereich der Produktseiten, die Inhaltsverzeichnisse in AGB,
     Widerruf und Datenschutz und die Gewichtstafel der Startseite. Alle
     standen einfach mit. `clip` schneidet genauso ab, ohne einen
     Scrollbereich aufzumachen. */
  overflow-x: clip;
}

img, picture, video, canvas, svg { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }

/* --- Typografie --------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 800;
  font-stretch: 112%;          /* breit wie die Wortmarke im Logo */
  line-height: .96;
  letter-spacing: -.02em;
  text-wrap: balance;
}

/* Bemessen auf zwei Zeilen: „PRÄZISION TRIFFT" muss in einer Zeile stehen,
   sonst wächst der Auftakt über die Bildschirmhöhe und die Ware beginnt
   erst hinter der Falz. */
.d-xl {
  font-size: clamp(2.6rem, 7.3vw, 7rem);
  font-weight: 900;
  font-stretch: 118%;
  line-height: .88;
  letter-spacing: -.035em;
  text-transform: uppercase;
}
/* `.d-l` trägt die Abschnittsüberschriften und steht fast immer zweizeilig.
   **Zeilenhöhe von .94 auf 1 angehoben (6.9.2026):** Unter 1 liegen die
   Grundlinien enger als die Schrift hoch ist — bei Versalien mit Umlaut
   („ÜBER DAS TRAINING / DAZUGEKOMMEN") stoßen die Punkte an die Zeile
   darüber, und zwei Zeilen lesen sich als eine gestapelte Fläche. Der
   Betreiber über die Athletenseite: „zwei Überschriften übereinandergestapelt
   und auch richtig kacke gemacht."

   1 ist immer noch eng — bei 72 px sind das 72 px Grundlinienabstand, ein
   Display-Wert. Fließtext läge bei 1.5. */
.d-l {
  font-size: clamp(1.95rem, 5.4vw, 4.5rem);
  font-stretch: 115%;
  line-height: 1;
  letter-spacing: -.028em;
  text-transform: uppercase;
}
.d-m {
  font-size: clamp(1.75rem, 3.4vw, 2.85rem);
  line-height: 1;
  letter-spacing: -.022em;
  text-transform: uppercase;
}
.d-s {
  font-size: clamp(1.2rem, 1.7vw, 1.5rem);
  font-stretch: 105%;
  line-height: 1.15;
  letter-spacing: -.012em;
}

/* Technisches Label — die Sprache eines Datenblatts */
.label {
  font-family: var(--f-mono);
  font-size: .7rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.label--accent { color: var(--accent-br); }

.lead {
  font-size: clamp(1.0625rem, 1.5vw, 1.25rem);
  line-height: 1.6;
  color: var(--fg-mute);
  max-width: 46ch;
}
.prose { max-width: 65ch; }
.prose p + p { margin-top: var(--s-4); }
/* h2 UND h3: Die Rechtstexte gliedern seit dem 16.8.2026 in h2 — ihre
   Abschnitte standen direkt unter dem h1 und übersprangen damit eine Stufe.
   Sichtbar ändert sich nichts, die Größe hängt an dieser Regel. */
.prose h2,
.prose h3 { margin: var(--s-7) 0 var(--s-3); font-size: 1.3rem; font-stretch: 105%; }
.prose a { color: var(--accent-br); text-decoration: underline; text-underline-offset: .18em; }
.prose strong { color: var(--fg); font-weight: 600; }
.prose ul { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-4); }
.prose ul li { padding-left: 1.15rem; position: relative; color: var(--fg-mute); }
.prose ul li::before {
  content: ''; position: absolute; left: 0; top: .72em;
  width: .4rem; height: 1px; background: var(--accent);
}

.num { font-variant-numeric: tabular-nums; }
/* Mono trägt oft lange, umbruchfeindliche Zeichenketten (URLs, IDs) */
.mono { font-family: var(--f-mono); overflow-wrap: anywhere; }

/* Archivo ist eine Variable Font: auf schmalen Geräten laufen die Versalien
   enger, statt dass lange Wörter wie AUSFÜHRUNGEN die Zeile sprengen. */
@media (max-width: 560px) {
  .d-xl { font-stretch: 100%; }
  .d-l  { font-stretch: 98%; }
  .d-m  { font-stretch: 98%; }
}

/* --- Raster ------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.grid { display: grid; gap: var(--s-6); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}

/* --- Raster, dessen letzte Zeile mittig steht --------------------------
   Für Karten, deren Zahl sich ändert und die nicht aufgehen: fünf Athleten
   in drei Spalten stünden 3 + 2, mit einer Lücke rechts unten, und eine
   Lücke im Raster liest sich als „hier fehlt jemand". Mittig gestellt ist
   dieselbe Zeile eine Reihe für sich.

   Das Raster hat doppelt so viele Spuren wie Spalten, jede Karte belegt
   zwei. Die erste Karte einer unvollständigen letzten Zeile rückt um eine
   Spur ein, bei einer Karte um zwei. Keine Zahl ist getippt: Das gilt für
   vier, fünf oder sieben Karten gleich. */
.grid-mitte { grid-template-columns: repeat(6, 1fr); }
.grid-mitte > * { grid-column: span 2; }
@media (min-width: 901px) {
  .grid-mitte > :nth-child(3n+1):nth-last-child(2) { grid-column: 2 / span 2; }
  .grid-mitte > :nth-child(3n+1):last-child        { grid-column: 3 / span 2; }
}
@media (min-width: 561px) and (max-width: 900px) {
  .grid-mitte { grid-template-columns: repeat(4, 1fr); }
  .grid-mitte > :nth-child(2n+1):last-child { grid-column: 2 / span 2; }
}
@media (max-width: 560px) {
  .grid-mitte { grid-template-columns: 1fr; }
  .grid-mitte > * { grid-column: auto; }
}

/* --- Ware bleibt zweispaltig, auch auf dem Telefon ---------------------
   Die Regel darüber setzt unter 560 px alles einspaltig — und das ist für
   Textkacheln richtig. Für Produkte ist es der Unterschied zwischen einem
   Laden und einem Katalog: Gemessen am 25.8.2026 war das Sortiment auf
   390 px **2636 px hoch**, also 659 px je Karte, und die Zubehörzeile
   1383 px. Zusammen vier Bildschirme für sechs Artikel.

   Wer auf dem Handy einkauft, will sehen, dass es mehr als eines gibt.
   Zwei nebeneinander sagen „Auswahl", eines untereinander sagt „Seite".
   Jeder Shop dieser Größenordnung macht es so.

   `:has(> .card)` trifft genau die Raster mit Ware — nicht die
   Vertrauensleiste, nicht die Bildkacheln der Story, nicht die
   Athletenkarten. Ohne diese Bedingung würde die Regel Textblöcke auf
   halbe Breite quetschen, die dort schon knapp stehen. */
@media (max-width: 560px) {
  .grid:has(> .card) {
    grid-template-columns: repeat(2, 1fr);
    /* Enger als am Schreibtisch: Bei 390 px Breite bleiben je Karte rund
       170 px, und davon darf der Zwischenraum nicht die Hälfte nehmen. */
    gap: var(--s-4);
  }
}

.stack { display: flex; flex-direction: column; }
.row   { display: flex; align-items: center; }

.section { padding-block: var(--sec-y); position: relative; }
.section--tight { padding-block: var(--sec-tight); }

/* Der Rhythmus — drei Stufen, nicht zwei
   ------------------------------------------------------------------
   Gemessen am 18.8.2026 lagen alle elf Abschnitte der Startseite
   zwischen 0,94 und 1,68 Bildschirmen. Das ist kein Takt, das ist ein
   Fließband: Was formal gleich aussieht, liest das Auge als Liste, und
   Listen überfliegt man.

   `--knapp` ist deshalb keine vierte Abstandsvariante, sondern eine
   Gegenstimme. Sie gehört an Abschnitte, die nur eine Sache sagen —
   eine Zahl, eine Zeile, ein Bild — und die dadurch den Abschnitt
   davor und danach tragen helfen.

   Der Selbsttest hält fest, dass nie drei gleiche Stufen aufeinander
   folgen. Ohne diese Prüfung wandert der Rhythmus bei der nächsten
   Änderung still wieder zurück ins Gleichmaß. */
.section--knapp { padding-block: var(--sec-knapp); }
.section--gross { padding-block: calc(var(--sec-y) * 1.35); }

/* Sektionskopf: Mono-Label links, Titel rechts — Datenblatt-Logik */
.sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5);
  /* Bis zum 1.10.2026 trug der Abstand nach oben das Seil (9 px). Das Seil
     ist als Trenner entfallen (components.css), der Abstand bleibt ohne
     die 9 px. */
  padding-top: var(--s-5);
  margin-bottom: var(--s-7);
}
@media (min-width: 860px) {
  .sec-head { grid-template-columns: 13rem minmax(0, 1fr); gap: var(--s-7); align-items: start; }
  .sec-head .label { padding-top: .55rem; }
}
.sec-head__body { display: flex; flex-direction: column; gap: var(--s-4); }

/* Sektionskopf neben der Ware statt darüber -----------------------------
   Für Abschnitte mit nur zwei Artikeln. Zwei Karten in einer Vierer-Zeile
   lassen die halbe Breite leer, und auf dunklem Grund liest sich eine
   leere Hälfte als fehlender Inhalt, nicht als Ruhe. Der Kopf füllt sie —
   und das Verhältnis stimmt dann auch: Zubehör bekommt weniger Fläche als
   die vier Handschuhe darüber. */
.zub {
  display: grid;
  gap: var(--s-6);
  padding-top: var(--s-5);   /* ohne die 9 px des Seils, seit 1.10.2026 */
}
@media (min-width: 900px) {
  .zub {
    grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr);
    column-gap: clamp(2rem, 5vw, 4.5rem);
    align-items: start;
  }
}
.zub__text {
  display: flex; flex-direction: column;
  gap: var(--s-4); align-items: flex-start;
}
.zub__text .lead { max-width: 40ch; }

/* --- Hilfsklassen ------------------------------------------------------- */
.visually-hidden {
  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 {
  position: absolute; top: -100%; left: var(--s-4); z-index: 999;
  background: var(--accent); color: var(--bone);
  padding: var(--s-3) var(--s-5); font-family: var(--f-mono); font-size: .8rem;
}
.skip-link:focus { top: var(--s-4); }

:focus-visible {
  outline: 2px solid var(--accent-br);
  outline-offset: 3px;
}

/* --- Was ein Touchgerät nicht bezahlen kann ------------------------------
   Milchglas ist auf dem Handy der teuerste Effekt, den diese Website hat.
   `backdrop-filter` heißt: Der Bereich hinter der Fläche wird kopiert,
   weichgezeichnet und wieder zusammengesetzt — in JEDEM Bild. Bei der
   Kopfzeile ist das die volle Bildschirmbreite, auf einem iPhone mit
   dreifacher Pixeldichte rund 1170 × 220 Pixel, und zwar durchgehend
   während des gesamten Scrollens. Auf Produktseiten liegt unten noch die
   feste Kaufleiste mit derselben Rechnung.

   Am Schreibtisch fällt das nicht auf, dort rechnet eine Grafikkarte mit.
   Auf dem Gerät ist es der Unterschied zwischen flüssig und zäh — und zwar
   nicht an einer Stelle, sondern immer.

   Ersetzt wird es durch eine deckende Fläche aus denselben Tokens. Der
   Zweck des Milchglases war, dass die Leiste sich vom Inhalt abhebt; das
   tut eine deckende Fläche besser. Der Blur bleibt dort, wo nicht gescrollt
   wird, während er zu sehen ist — bei den Überlagerungen.               */
@media (hover: none) {
  .hdr.is-stuck,
  .buybar {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--bg);
  }
  .hero__tag__body,
  .gallery__lupe {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--surface);
  }
  /* Überlagerungen: Der Schleier ist ohnehin fast deckend, der Weichzeichner
     darunter kostet dort nur noch Zeit beim Öffnen. */
  .lbox, .modal, .scrim {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Ein Mischmodus zwingt den Browser, für die Fläche darunter einen eigenen
     Durchgang zu rechnen. Beim Wasserzeichen im Auftakt geht es um 3,8 %
     Deckkraft auf 78 vw Breite — der Unterschied ist unsichtbar, die
     Rechnung nicht. */
  .hero__mark { mix-blend-mode: normal; }
}

/* --- Was ein Finger statt eines Zeigers bekommt --------------------------
   Auf dem Handy fällt die gesamte Zeige-Ebene dieser Website weg: Die Karte
   hebt nicht an, der Füllwisch im Knopf läuft nicht, der Unterstrich baut
   sich nicht auf. Übrig bliebe eine Seite, die auf Berührung gar nichts
   sagt — und genau so liest sie sich dann auch: als Bild, nicht als
   Bedienoberfläche.

   Der Ersatz ist der Druckzustand. Er muss dafür allerdings selbst sichtbar
   sein: Android und iOS legen von sich aus ein graues Rechteck über das
   angetippte Element, das mit einer Verzögerung kommt und die Form des
   Elements verfehlt. Das wird abgeschaltet und durch eine eigene Antwort
   ersetzt — sofort, kurz, auf der Grafikeinheit. */
a, button, [role='button'], summary, label, input, select, textarea {
  -webkit-tap-highlight-color: transparent;
}
/* Doppeltipp-Zoom kostet auf Bedienelementen 300 ms Wartezeit vor jedem
   Klick — auf Textflächen bleibt er, dort ist Zoomen legitim. */
a, button, [role='button'], .card, .acc__btn { touch-action: manipulation; }

/* Der Druck: eine Regel für alles, was angetippt werden kann und keinen
   eigenen Zustand mitbringt. Die Bausteine mit eigener Antwort (.btn,
   .chip, .wahl, .card__quick-btn) stehen weiter unten und schlagen sie. */
@media (hover: none) {
  .tlink:active,
  .mnav__item a:active,
  .ftr__recht a:active,
  .drawer__weiter:active,
  .cart-line__rm:active,
  .sparhint__zu:active,
  .icon-btn:active { color: var(--accent-br); }

  .card:active,
  .upsell:active,
  .trust__cell:active,
  .social-konto:active,
  .cart-add__zeile:active { transform: scale(.985); }

  .card, .upsell, .trust__cell, .social-konto, .cart-add__zeile {
    transition: transform .13s var(--ease-out), border-color .2s var(--ease);
  }
  /* Der Aufklappknopf trägt die Antwort auf dem Zeichen, nicht auf der
     ganzen Zeile: Eine Frage, die beim Antippen schrumpft, sieht aus, als
     wäre sie weggerutscht. */
  .acc__btn:active { color: var(--accent-br); }
  .acc__btn:active .acc__sign { transform: scale(.86); }
  .acc__sign { transition: transform .13s var(--ease-out); }
}

::selection { background: var(--accent); color: var(--bone); }

/* --- Bewegungs-Grundlage ------------------------------------------------ */
/* Auf schmalen Geräten läuft dieselbe Einblendung kürzer und mit kleinerem
   Weg. Der Grund steht bei --t-reveal: Ein Wisch bewegt anteilig weit mehr
   Seite als ein Mausrad, und was gegen das Scrollen zu langsam ist, wird
   nicht als Einblendung wahrgenommen, sondern als Nachladen. */
/* Am 11.8.2026 stand hier `.5s` und `9px` — kürzer und kleiner als am
   Schreibtisch, mit der Absicht, dass die Einblendung fertig wird, bevor das
   Element durchgescrollt ist. Das war der falsche Hebel und hat genau das
   erzeugt, was danach beanstandet wurde: Neun Pixel über eine halbe Sekunde
   sind auf einem Telefon keine Bewegung mehr, sondern ein Aufblitzen. Was zu
   spät kommt, muss früher anfangen — nicht kürzer werden. Der Vorlauf steht
   jetzt im Beobachter (initReveal), und die Bewegung darf hier wieder das
   sein, was sie am Schreibtisch ist: eher etwas mehr Weg, weil derselbe Weg
   auf 390 px Breite anteilig weniger hergibt. */
@media (max-width: 899px) {
  :root { --t-reveal: .68s; --reveal-weg: 18px; --t-mask: .8s; }
}
[data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-weg));
  transition:
    opacity var(--t-reveal) var(--ease-out),
    transform var(--t-reveal) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal].is-in { opacity: 1; transform: none; }
/* Zeilen einer durchgehenden Liste blenden nur auf — ihre Trennlinie darf
   nicht mitwandern. Muss nach der Grundregel stehen, beide haben dieselbe
   Spezifität. */
[data-reveal='ruhig'] { transform: none; }

/* --- Eingänge nach Art und Lage (1.10.2026) -----------------------------
   Vergeben in motion.js (AUTO_TEILE). Jede Art hat eine Bewegung, die zu
   ihr passt; alle mit derselben Kurve (--ease-out), damit die Seite eine
   Handschrift hat und nicht sechs. Nur transform, opacity und — für
   Überschriften mit Zeiger — eine leichte Unschärfe. */

/* Überschrift: hebt sich um ein Drittel ihrer Größe und wird scharf. Die
   Unschärfe nur mit Zeiger: Auf dem Telefon ist jeder Filter ein eigener
   Rasterdurchgang (Projektnotiz zu den iOS-Render-Bremsen). */
[data-reveal='titel'] { transform: translateY(.35em); }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  [data-reveal='titel'] {
    filter: blur(6px);
    transition:
      opacity var(--t-reveal) var(--ease-out),
      transform var(--t-reveal) var(--ease-out),
      filter calc(var(--t-reveal) * .8) var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
  }
  [data-reveal='titel'].is-in { filter: none; }
}
/* Kleine Zeile über der Überschrift: kommt von links, wo sie anfängt. */
[data-reveal='label'] { transform: translateX(-12px); }
/* Spalten am Schreibtisch kommen von ihrer Seite. */
[data-reveal='links']  { transform: translateX(-24px); }
[data-reveal='rechts'] { transform: translateX(24px); }
/* Handlung: kleiner Weg, sie kommt nach dem Text (Verzögerung im Skript). */
[data-reveal='knopf'] { transform: translateY(8px); }
/* Bild im Rahmen: Der Rahmen blendet auf, das Bild darin setzt sich von
   leicht vergrößert auf seine Größe — wie eine Kamera, die scharf stellt.
   Ohne clip-path: Der wäre die naheliegende Enthüllung, ist auf dem iPhone
   unter einer Übergangsbewegung aber spürbar zäh. */
[data-reveal='bild'] { transform: none; }
[data-reveal='bild'] img {
  transform: scale(1.06);
  transition: transform calc(var(--t-reveal) * 1.7) var(--ease-out) var(--reveal-delay, 0ms);
}
[data-reveal='bild'].is-in img { transform: none; }
/* Ein Bild ohne Rahmen schrumpft nicht aus dem Rand, es wächst leicht an. */
img[data-reveal='bild'] { transform: scale(.97); }
[data-reveal].is-in { opacity: 1; transform: none; }

/* Ladechoreografie. Bis zum 5.8.2026 vergab playIntro() zwar die Klasse
   is-in, aber es gab keine Regel dazu — sichtbar wurde davon nur die
   Überschrift, weil sie über .line-mask läuft. Alles Übrige stand
   schlagartig da. Das ist der Grundzustand, der gefehlt hat. */
[data-intro] {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity .6s var(--ease-out),
    transform .6s var(--ease-out);
}
[data-intro].is-in { opacity: 1; transform: none; }

/* Die Zeilenmaske bringt ihre eigene Bewegung mit und versteckt ihren Inhalt
   bereits selbst (overflow + translateY(105%)). Eine Überschrift darf daher
   weder zusätzlich wandern noch aufblenden: Der Sinn der Maske ist, dass die
   Zeile hinter einer Kante hervorkommt — blendet sie dabei ein, sieht man sie
   durch die Maske hindurch erscheinen und der Effekt ist hin. Diese Ausnahme
   hält zugleich alle Unterseiten unverändert, deren einziges [data-intro]
   genau so ein <h1> ist. */
[data-intro]:has(.line-mask) { opacity: 1; transform: none; }

/* Zeilenweises Aufsteigen für Überschriften.
   Die Maske bekommt oben Luft, sonst kappt sie bei engem Zeilenabstand die
   Punkte deutscher Versalumlaute (Ä, Ö, Ü). */
.line-mask {
  display: block;
  overflow: hidden;
  padding-top: .14em;
  margin-top: -.14em;
}
.line-mask > span {
  display: block;
  transform: translateY(105%);
  transition: transform var(--t-mask, 1s) var(--ease-out);
  transition-delay: var(--line-delay, 0ms);
}
.is-in .line-mask > span,
.line-mask.is-in > span { transform: none; }

/* --- Reduzierte Bewegung -------------------------------------------------
   „Reduced motion" heißt **weniger und sanfter, nicht aus.** Bis zum
   6.9.2026 stand hier der verbreitete Pauschalschalter
   `transition-duration: .01ms !important` auf allem — der nimmt aber auch
   die Übergänge, die *Verständnis* stützen: Ein Knopf, dessen Farbe
   springt, sagt weniger als einer, dessen Farbe überblendet. Weg soll die
   Ortsveränderung, nicht die Rückmeldung.

   Umgesetzt über die **Eigenschaftsliste** statt über die Dauer: Was in
   `transition-property` steht, blendet weiter über; alles andere —
   `transform`, `clip-path`, `width`, `max-height`, `visibility` — springt
   sofort auf seinen Zielwert.

   `transition-delay: 0s` gehört dazu. Verzögerungen sind Teil einer
   Choreografie; ohne Bewegung wartet ein Element sonst auf etwas, das nicht
   mehr stattfindet. Genau der Fall, den die alte Sonderregel für Menü und
   Schublade abfangen musste: Beide hielten ihre Sichtbarkeit zurück, bis
   das Zufahren durch war, und wären eine halbe Sekunde lang unsichtbar
   bedienbar geblieben. Global gelöst braucht es die Sonderregel nicht mehr.

   Keyframes bleiben abgeschaltet: Sie tragen auf dieser Website
   ausschließlich Bewegung (Schweben, Licht, Laufband, Stoß). */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;

    /* Was bleiben darf: Farbe, Deckkraft, Kante, Schatten. Alles, was den
       Zustand erklärt, ohne den Ort zu ändern. */
    transition-property:
      opacity, color, background-color, border-color, box-shadow,
      fill, stroke, outline-color !important;
    transition-delay: 0s !important;
  }

  /* Was ohne Skript oder ohne Einblendung sonst unsichtbar bliebe. */
  [data-reveal] { opacity: 1; transform: none; }
  [data-reveal='bild'] img { transform: none; }
  [data-intro]  { opacity: 1; transform: none; }
  .line-mask > span { transform: none; }
}

/* --- Warum überall svh und nirgends vh ---------------------------------
   Auf iOS fährt die Adressleiste beim Scrollen ein und aus. Damit ändert
   sich `vh` — und mit ihm jede Höhe, die daran hängt. Der Betreiber am
   25.8.2026: „sobald ich runterscrolle und oben die URL klein wird, dann
   springt irgendwie alles in groß rein … die Seite buggt komplett."

   Drei Einheiten stehen zur Wahl:

     vh   die alte, springt mit der Leiste
     dvh  „dynamic" — springt absichtlich mit, also dasselbe Problem
     lvh  „large" — die Höhe OHNE Leisten; Inhalt landet darunter
     svh  „small" — die Höhe MIT Leisten, konstant

   Diese Website rechnet ausschließlich mit **svh**. Ein Element, das so
   hoch ist wie das kleinste Fenster, passt immer und bewegt sich nie.
   Fährt die Leiste ein, wird das Fenster größer als der Abschnitt — man
   sieht darunter etwas mehr. Das ist stabil und richtig.

   **Zweite Falle, dieselbe Ursache:** Eine Media-Query mit `max-height`
   schaltet ganze Regelblöcke um, wenn die Leiste die Schwelle kreuzt. Ein
   iPhone 12 hat 664 px mit Leiste und 740 ohne — eine Schwelle bei 700 px
   liegt genau dazwischen. Solche Schwellen gehören geprüft: Sie sind nur
   dann sicher, wenn kein Gerät sie beim Scrollen überquert.            */
