/* DropshipAgent Design System — gebaut aus:
 *   06-design-tokens/build/css/tokens.css  (351 Variablen)
 *   09-components/src/styles.css           (109 Variablen benutzt, 20 lokale Aliasse)
 * Nicht von Hand bearbeiten. Gebaut: 2026-10-08T06:19:26.537Z
 */

/**
 * Do not edit directly, this file was auto-generated.
 */

:root {
  --color-primary: #0c0b16; /** Primary brand color - ink (near-black) for dark backgrounds, headers, main sections. Replaced legacy navy #181728 in the June 2026 rebrand. */
  --color-primary-hover: rgba(12, 11, 22, 0.64); /** Primary color hover state (64% opacity) */
  --color-navy-legacy: #181728; /** DEPRECATED - legacy dark navy, superseded by ink (#0C0B16) in the June 2026 rebrand. Kept for documentation and migration reference only; do not use in new work. */
  --color-secondary: #d6fd70; /** Secondary accent color - lime green for CTAs, highlights, accents */
  --color-secondary-hover: #e1faa0; /** Secondary color hover state (lighter lime green) */
  --color-secondary-16: rgba(214, 253, 112, 0.16); /** Secondary at 16% opacity - subtle backgrounds, badges on dark */
  --color-background-dark-primary: #0c0b16; /** Main site background - ink (near-black) */
  --color-background-dark-secondary: #2d2c43; /** Mobile menu, card backgrounds on dark */
  --color-background-dark-elevated: #10122a; /** Upper stop of the dark-surface gradient; elevated dark panels */
  --color-background-light: #e2e3e7; /** Alternate sections in light mode */
  --color-background-grey: #f3ede9; /** Subtle backgrounds, card fills */
  --color-background-grey-50: rgba(243, 237, 233, 0.5); /** Grey at 50% opacity */
  --color-text-title-dark: #0c0b16; /** Headings on light backgrounds */
  --color-text-title-light: #ffffff; /** Headings on dark backgrounds */
  --color-text-body-on-dark: rgba(255, 255, 255, 0.64); /** Paragraph text on dark backgrounds */
  --color-text-body-on-light: rgba(12, 11, 22, 0.64); /** Paragraph text on light backgrounds */
  --color-text-muted: rgba(255, 255, 255, 0.48); /** Secondary information, captions */
  --color-text-placeholder: #4a5055; /** Form input placeholders */
  --color-utility-blue: #3140d3; /** Gradient end color, top header bar accent, special highlights */
  --color-utility-red: #fa4149; /** Error states, warnings, destructive actions */
  --color-gradient-signature: linear-gradient(90deg, #3140D3, #D6FD70); /** Signature accent - dividers, heading underlines, accent lines */
  --color-gradient-hero-cta: linear-gradient(105deg, #D6FD70 0%, #BCEF74 35%, #8E9AF1 95%); /** Gradient buttons and hero highlights (3-stop). Solid fallback: #D6FD70 */
  --color-gradient-hero-cta-rich: linear-gradient(105deg, #D6FD70 0%, #BCEF74 28%, #7E8BEE 72%, #5563E8 100%); /** Rich 4-stop variant - large hero surfaces, feature CTAs, hover state of hero-cta */
  --color-gradient-glow: linear-gradient(135deg, #D6FD70, #3140D3 60%, transparent); /** Decorative glow - background effects, card edges */
  --color-gradient-dark-surface: linear-gradient(180deg, #10122A 0%, #0C0B16 42%, #0C0B16 100%); /** Hero/dark section backgrounds. Solid fallback: #0C0B16 */
  --color-gradient-stop-lime-soft: #bcef74; /** Gradient stop - softened lime */
  --color-gradient-stop-periwinkle: #8e9af1; /** Gradient stop - periwinkle */
  --color-gradient-stop-periwinkle-deep: #7e8bee; /** Gradient stop - deep periwinkle (4-stop variant) */
  --color-gradient-stop-blue-mid: #5563e8; /** Gradient stop - mid blue (4-stop variant end) */
  --color-border-light: rgba(255, 255, 255, 0.16); /** Borders on dark backgrounds */
  --color-border-dark: rgba(12, 11, 22, 0.16); /** Borders on light backgrounds */
  --color-white-8: rgba(255, 255, 255, 0.08); /** White at 8% opacity */
  --color-white-16: rgba(255, 255, 255, 0.16); /** White at 16% opacity */
  --color-white-32: rgba(255, 255, 255, 0.32); /** White at 32% opacity */
  --color-white-48: rgba(255, 255, 255, 0.48); /** White at 48% opacity */
  --color-white-64: rgba(255, 255, 255, 0.64); /** White at 64% opacity */
  --color-white-100: #ffffff; /** Pure white */
  --color-black-16: rgba(12, 11, 22, 0.16); /** Black at 16% opacity */
  --color-black-32: rgba(12, 11, 22, 0.32); /** Black at 32% opacity */
  --color-black-48: rgba(12, 11, 22, 0.48); /** Black at 48% opacity */
  --color-black-64: rgba(12, 11, 22, 0.64); /** Black at 64% opacity */
  --color-black-100: #0c0b16; /** Brand black (ink) */
  --color-green-8: rgba(214, 253, 112, 0.08); /** Lime green at 8% (faint tints on dark) */
  --color-green-16: rgba(214, 253, 112, 0.16); /** Lime green at 16% (subtle backgrounds) */
  --color-green-32: rgba(214, 253, 112, 0.32); /** Lime green at 32% (glow edges, emphasis on dark) */
  --color-green-64: #e1faa0; /** Lime green lighter (hover states) */
  --color-green-100: #d6fd70; /** Lime green - primary accent */
  --color-status-danger-on-dark: #fb6167; /** DERIVED from utility.red #FA4149: same hue, same saturation, lightness 0.618 -> 0.682. Measures 6.52:1 on #0C0B16 and 4.51:1 on #2D2C43. utility.red itself fails the card at 3.81:1, which is why dark needs its own value. */
  --color-status-danger-on-light: #cd050e; /** DERIVED from utility.red #FA4149: same hue and saturation, lightness 0.618 -> 0.413. Measures 4.52:1 on #E2E3E7 and 4.99:1 on #F3EDE9. utility.red fails both light grounds (2.77:1 / 3.06:1). */
  --color-status-warning-on-dark: #e0b341; /** RATIFIED from the live DSA customer portal. Measures 9.95:1 on #0C0B16 and 6.89:1 on #2D2C43. Chosen over the crew app's #FCD34D, which also passes (13.55:1 / 9.38:1) but is a Tailwind default rather than a value anyone decided. */
  --color-status-warning-on-light: #8a5a12; /** RATIFIED from the supplier portal prototype. Measures 4.61:1 on #E2E3E7 and 5.10:1 on #F3EDE9. Both on-dark warning candidates fail here (1.69:1 / 1.24:1), which is why warning needs two values. */
  --color-status-success-on-dark: #46b993; /** NEW. The only slot with no candidate in production, because both existing apps use lime for success -- which collides with the rule that #D6FD70 is reserved for CTAs, highlights and interactive elements. Hue 160 degrees against lime's 77, saturation 0.45. Measures 8.02:1 on #0C0B16 and 5.55:1 on #2D2C43. Distance to lime: 163 in sRGB at full tone and 26 at a 16 percent tint. Simulated colour-blindness distance to danger-on-dark: 57 protanopia, 58 deuteranopia -- the best separation among the muted candidates, and muted on purpose so a success badge does not compete with the lime CTA. */
  --color-status-success-on-light: #1f6d3f; /** RATIFIED from the supplier portal prototype. Measures 4.94:1 on #E2E3E7 and 5.46:1 on #F3EDE9. */
  --color-status-info-on-dark: #858ee5; /** DERIVED from utility.blue #3140D3: same hue and saturation, lightness 0.510 -> 0.709. Measures 6.51:1 on #0C0B16 and 4.51:1 on #2D2C43. utility.blue itself is unusable on dark at 2.61:1 and 1.81:1 -- it fails even the 3:1 threshold for a non-text UI element on the card. On light it is fine and is used directly. */
  --spacing-0: 0; /** Zero spacing */
  --spacing-1: 4px; /** 1 unit — button icon gap (gap: 4px in .btn) */
  --spacing-2: 8px; /** 2 units — small button padding, form label margin-bottom */
  --spacing-3: 12px; /** 3 units — button vertical padding, small button padding-right */
  --spacing-4: 16px; /** 4 units — button horizontal padding, form input vertical padding, motto badge horizontal padding, font-size base */
  --spacing-5: 20px; /** 5 units — intermediate spacing */
  --spacing-6: 24px; /** 6 units — container gutter (1.5rem), feature card number offset, motto badge border-radius */
  --spacing-8: 32px; /** 8 units — card padding (32px), section spacing --sp-xxs */
  --spacing-10: 40px; /** 10 units — container inline padding (1200-1365px breakpoint) */
  --spacing-12: 48px; /** 12 units — section spacing --sp-xs, mobile gutter, mobile --sp-xl/--sp-lg */
  --spacing-16: 64px; /** 16 units — section spacing --sp-sm */
  --spacing-20: 80px; /** 20 units — section spacing --sp-md */
  --spacing-30: 120px; /** 30 units — section spacing --sp-lg */
  --spacing-40: 160px; /** 40 units — section spacing --sp-xl (largest section padding) */
  --spacing-px: 1px; /** Single pixel — borders, dividers */
  --spacing-0-5: 2px; /** Half unit — fine adjustments */
  --spacing-1-5: 6px; /** 1.5 units — motto badge bottom padding */
  --section-spacing-xxs: 32px; /** --sp-xxs: Minimal section spacing */
  --section-spacing-xs: 48px; /** --sp-xs: Extra small section spacing */
  --section-spacing-sm: 64px; /** --sp-sm: Small section spacing */
  --section-spacing-md: 80px; /** --sp-md: Medium section spacing */
  --section-spacing-lg: 120px; /** --sp-lg: Large section spacing */
  --section-spacing-xl: 160px; /** --sp-xl: Extra large section spacing */
  --section-spacing-mobile-xxs: 32px; /** --sp-xxs mobile: unchanged */
  --section-spacing-mobile-xs: 32px; /** --sp-xs mobile */
  --section-spacing-mobile-sm: 40px; /** --sp-sm mobile */
  --section-spacing-mobile-md: 40px; /** --sp-md mobile */
  --section-spacing-mobile-lg: 48px; /** --sp-lg mobile */
  --section-spacing-mobile-xl: 48px; /** --sp-xl mobile */
  --radius-none: 0; /** No radius — sharp corners */
  --radius-sm: 4px; /** Small radius — subtle rounding. KEINE Zweckzuordnung mehr: welcher Radius fuer Eingabefelder gilt, sagt semantic/shape.json (radius.input). Zwei Stellen, die denselben Zweck beanspruchen, widersprechen sich irgendwann. */
  --radius-md: 8px; /** Medium radius — buttons (border-radius: 8px), standard rounding */
  --radius-lg: 12px; /** Large radius — intermediate rounding */
  --radius-xl: 16px; /** Extra large radius — cards and images (--rd-0: 16px) */
  --radius-2xl: 24px; /** 2X large radius — motto/badge component, modals */
  --radius-full: 9999px; /** Full radius — pills, circles, avatar crops */
  --shadow-card: 0px 4px 10px 0px rgba(0, 0, 0, 0.05), 0px 34px 20px 0px rgba(0, 0, 0, 0.03); /** Standard card shadow on light backgrounds — dual layer for subtle depth */
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); /** Small shadow — subtle elevation for interactive elements */
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1); /** Medium shadow — dropdowns, popovers */
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); /** Large shadow — modals, floating panels */
  --shadow-none: none; /** No shadow — cards on dark backgrounds (dark cards have no shadow) */
  --container-gutter: 24px; /** Container gutter (1.5rem) — standard side padding */
  --container-gutter-mobile: 48px; /** Mobile container gutter (max-width: 991px) */
  --container-inline-padding-compact: 40px; /** Container inline padding at 1200-1365px breakpoint */
  --font-family-primary: 'TWK Everett', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; /** Primary font family - TWK Everett for all text */
  --font-family-fallback: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; /** System font fallback stack */
  --font-weight-light: 300; /** Light weight - body text, paragraphs, general content */
  --font-weight-medium: 500; /** Medium weight - headings, buttons, navigation, emphasis, links */
  --font-size-xs-desktop: 12px; /** Extra small - captions, badges, fine print */
  --font-size-xs-mobile: 10px; /** Extra small mobile */
  --font-size-sm-desktop: 16px; /** Small text - secondary content, labels */
  --font-size-sm-mobile: 14px; /** Small text mobile */
  --font-size-base-desktop: 20px; /** Base body text size */
  --font-size-base-mobile: 16px; /** Base body text mobile */
  --font-size-h6-desktop: 20px; /** Heading 6 */
  --font-size-h6-mobile: 16px; /** Heading 6 mobile */
  --font-size-h5-desktop: 24px; /** Heading 5 */
  --font-size-h5-mobile: 20px; /** Heading 5 mobile */
  --font-size-h4-desktop: 32px; /** Heading 4 - Card titles */
  --font-size-h4-mobile: 24px; /** Heading 4 mobile */
  --font-size-h3-desktop: 40px; /** Heading 3 - Subsection titles */
  --font-size-h3-mobile: 28px; /** Heading 3 mobile */
  --font-size-h2-desktop: 56px; /** Heading 2 - Section titles */
  --font-size-h2-mobile: 36px; /** Heading 2 mobile */
  --font-size-h1-desktop: 72px; /** Heading 1 - Hero/Page titles */
  --font-size-h1-mobile: 48px; /** Heading 1 mobile */
  --font-line-height-none: 1; /** Line height 1 - headlines, single line */
  --font-line-height-tight: 1.2; /** Tight line height - H3 */
  --font-line-height-snug: 1.25; /** Snug line height - H4 */
  --font-line-height-normal: 1.33; /** Normal body line height */
  --font-line-height-h5: 1.334; /** H5 line height */
  --font-line-height-relaxed: 1.4; /** Relaxed - body text, paragraphs */
  --font-letter-spacing-h1-desktop: -4px; /** H1 letter spacing desktop */
  --font-letter-spacing-h1-mobile: -3px; /** H1 letter spacing mobile */
  --font-letter-spacing-h2-desktop: -3px; /** H2 letter spacing desktop */
  --font-letter-spacing-h2-mobile: -2px; /** H2 letter spacing mobile */
  --font-letter-spacing-h3: -2px; /** H3 letter spacing */
  --font-letter-spacing-h4-desktop: -2px; /** H4 letter spacing desktop */
  --font-letter-spacing-h4-mobile: -1px; /** H4 letter spacing mobile */
  --font-letter-spacing-h5: -1px; /** H5 letter spacing */
  --font-letter-spacing-body: -0.25px; /** Body text letter spacing */
  --font-letter-spacing-small: 0; /** Small text letter spacing */
  --font-letter-spacing-motto: 1px; /** Uppercase motto/badge letter spacing */
  --font-text-style-h1-font-family: 'TWK Everett', sans-serif;
  --font-text-style-h1-font-weight: 500;
  --font-text-style-h1-font-size-desktop: 72px;
  --font-text-style-h1-font-size-mobile: 48px;
  --font-text-style-h1-line-height: 1;
  --font-text-style-h1-letter-spacing-desktop: -4px;
  --font-text-style-h1-letter-spacing-mobile: -3px;
  --font-text-style-h1-color: #ffffff;
  --font-text-style-h2-font-family: 'TWK Everett', sans-serif;
  --font-text-style-h2-font-weight: 500;
  --font-text-style-h2-font-size-desktop: 56px;
  --font-text-style-h2-font-size-mobile: 36px;
  --font-text-style-h2-line-height: 1;
  --font-text-style-h2-letter-spacing-desktop: -3px;
  --font-text-style-h2-letter-spacing-mobile: -2px;
  --font-text-style-h3-font-family: 'TWK Everett', sans-serif;
  --font-text-style-h3-font-weight: 500;
  --font-text-style-h3-font-size-desktop: 40px;
  --font-text-style-h3-font-size-mobile: 28px;
  --font-text-style-h3-line-height: 1.2;
  --font-text-style-h3-letter-spacing: -2px;
  --font-text-style-h4-font-family: 'TWK Everett', sans-serif;
  --font-text-style-h4-font-weight: 500;
  --font-text-style-h4-font-size-desktop: 32px;
  --font-text-style-h4-font-size-mobile: 24px;
  --font-text-style-h4-line-height: 1.25;
  --font-text-style-h4-letter-spacing-desktop: -2px;
  --font-text-style-h4-letter-spacing-mobile: -1px;
  --font-text-style-h5-font-family: 'TWK Everett', sans-serif;
  --font-text-style-h5-font-weight: 500;
  --font-text-style-h5-font-size-desktop: 24px;
  --font-text-style-h5-font-size-mobile: 20px;
  --font-text-style-h5-line-height: 1.334;
  --font-text-style-h5-letter-spacing: -1px;
  --font-text-style-h6-font-family: 'TWK Everett', sans-serif;
  --font-text-style-h6-font-weight: 500;
  --font-text-style-h6-font-size-desktop: 20px;
  --font-text-style-h6-font-size-mobile: 16px;
  --font-text-style-h6-line-height: 1.4;
  --font-text-style-body-font-family: 'TWK Everett', sans-serif;
  --font-text-style-body-font-weight: 300;
  --font-text-style-body-font-size-desktop: 20px;
  --font-text-style-body-font-size-mobile: 16px;
  --font-text-style-body-line-height: 1.4;
  --font-text-style-body-letter-spacing: -0.25px;
  --font-text-style-body-color: rgba(255, 255, 255, 0.64);
  --font-text-style-body-sm-font-family: 'TWK Everett', sans-serif;
  --font-text-style-body-sm-font-weight: 300;
  --font-text-style-body-sm-font-size-desktop: 16px;
  --font-text-style-body-sm-font-size-mobile: 14px;
  --font-text-style-body-sm-line-height: 1.4;
  --font-text-style-body-sm-letter-spacing: 0;
  --font-text-style-body-xs-font-family: 'TWK Everett', sans-serif;
  --font-text-style-body-xs-font-weight: 300;
  --font-text-style-body-xs-font-size-desktop: 12px;
  --font-text-style-body-xs-font-size-mobile: 10px;
  --font-text-style-body-xs-line-height: 1.33;
  --font-text-style-body-xs-letter-spacing: 0;
  --font-text-style-motto-font-family: 'TWK Everett', sans-serif;
  --font-text-style-motto-font-weight: 500;
  --font-text-style-motto-font-size-desktop: 12px;
  --font-text-style-motto-font-size-mobile: 10px;
  --font-text-style-motto-line-height-desktop: 16px;
  --font-text-style-motto-line-height-mobile: 12px;
  --font-text-style-motto-letter-spacing: 1px;
  --font-text-style-motto-text-transform: uppercase;
  --font-text-style-button-font-family: 'TWK Everett', sans-serif;
  --font-text-style-button-font-weight: 500;
  --font-text-style-button-font-size-desktop: 16px;
  --font-text-style-button-font-size-mobile: 14px;
  --font-text-style-button-line-height: 24px;
  --font-text-style-button-letter-spacing: -0.5px;
  --font-text-style-button-small-font-family: 'TWK Everett', sans-serif;
  --font-text-style-button-small-font-weight: 500;
  --font-text-style-button-small-font-size-desktop: 14px;
  --font-text-style-button-small-font-size-mobile: 12px;
  --font-text-style-button-small-line-height: 16px;
  --contrast-dark-text-primary-on-canvas: 19.53; /** color.text.title-light on #0C0B16. */
  --contrast-dark-text-primary-on-raised: 13.52; /** color.text.title-light on #2D2C43. */
  --contrast-dark-text-secondary-on-canvas: 8.17; /** color.text.body-on-dark on #0C0B16. */
  --contrast-dark-text-secondary-on-raised: 6.5; /** color.text.body-on-dark on #2D2C43. */
  --contrast-dark-text-muted-on-canvas: 4.99; /** color.text.muted on #0C0B16. */
  --contrast-dark-text-muted-on-raised: 4.36; /** color.text.muted on #2D2C43. */
  --contrast-dark-status-danger-on-canvas: 6.52; /** color.status.danger-on-dark on #0C0B16. */
  --contrast-dark-status-danger-on-raised: 4.51; /** color.status.danger-on-dark on #2D2C43. */
  --contrast-dark-status-warning-on-canvas: 9.95; /** color.status.warning-on-dark on #0C0B16. */
  --contrast-dark-status-warning-on-raised: 6.89; /** color.status.warning-on-dark on #2D2C43. */
  --contrast-dark-status-success-on-canvas: 8.02; /** color.status.success-on-dark on #0C0B16. */
  --contrast-dark-status-success-on-raised: 5.55; /** color.status.success-on-dark on #2D2C43. */
  --contrast-dark-status-info-on-canvas: 6.51; /** color.status.info-on-dark on #0C0B16. */
  --contrast-dark-status-info-on-raised: 4.51; /** color.status.info-on-dark on #2D2C43. */
  --contrast-dark-pill-danger-text: 10.94; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-dark-pill-danger-edge: 3.65; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-dark-pill-warning-text: 9.82; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-dark-pill-warning-edge: 5; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-dark-pill-success-text: 10.3; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-dark-pill-success-edge: 4.23; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-dark-pill-info-text: 10.59; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-dark-pill-info-edge: 3.53; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-light-text-primary-on-canvas: 15.23; /** color.text.title-dark on #E2E3E7. */
  --contrast-light-text-primary-on-raised: 16.84; /** color.text.title-dark on #F3EDE9. */
  --contrast-light-text-secondary-on-canvas: 5.42; /** color.text.body-on-light on #E2E3E7. */
  --contrast-light-text-secondary-on-raised: 5.64; /** color.text.body-on-light on #F3EDE9. */
  --contrast-light-status-danger-on-canvas: 4.52; /** color.status.danger-on-light on #E2E3E7. */
  --contrast-light-status-danger-on-raised: 4.99; /** color.status.danger-on-light on #F3EDE9. */
  --contrast-light-status-warning-on-canvas: 4.61; /** color.status.warning-on-light on #E2E3E7. */
  --contrast-light-status-warning-on-raised: 5.1; /** color.status.warning-on-light on #F3EDE9. */
  --contrast-light-status-success-on-canvas: 4.94; /** color.status.success-on-light on #E2E3E7. */
  --contrast-light-status-success-on-raised: 5.46; /** color.status.success-on-light on #F3EDE9. */
  --contrast-light-status-info-on-canvas: 5.84; /** color.utility.blue on #E2E3E7. */
  --contrast-light-status-info-on-raised: 6.45; /** color.utility.blue on #F3EDE9. */
  --contrast-light-pill-danger-text: 12.71; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-light-pill-danger-edge: 3.77; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-light-pill-warning-text: 13.61; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-light-pill-warning-edge: 4.12; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-light-pill-success-text: 13.47; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-light-pill-success-edge: 4.36; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-light-pill-info-text: 13.1; /** Pill text (text.primary) on the 16 percent fill, on the card surface. */
  --contrast-light-pill-info-edge: 5.02; /** Pill border and dot at full tone against the 16 percent fill. 3:1 applies, not 4.5:1. */
  --contrast-text-on-accent: 16.88; /** Ink on the lime accent -- the pairing the template flags as the one that usually fails. */
  --minimum-body-text: 4.5;
  --minimum-large-text: 3;
  --minimum-ui-component: 3; /** Borders, icons, status dots -- WCAG 1.4.11. */
  --app-density: compact; /** Product density. A fulfilment table shows forty rows; marketing shows one message. Same brand, different job. */
  --app-pill-fill-tint: 0.16; /** Role colour at 16 percent as the fill. Carries the signal by hue. Unchanged from practice. */
  --app-pill-edge-tint: 1.0; /** Border and status dot at FULL tone, not 32 percent. Measured against the 16 percent fill: 3.53 to 5.02:1 -- above the 3:1 threshold that applies to a non-text UI element. At 32 percent the same edge measures 1.09 to 1.94:1. A border that needs 70 to 88 percent opacity to be visible is not a 32 percent border. */
  --app-state-cta-glow-tint: 0.32; /** Deckkraft des Leuchtens unter der Hauptaktion. Aus dem Kundenportal uebernommen: .dsa-cta:hover setzt box-shadow 0 0 24px var(--clr-green-32), und --clr-green-32 ist das Lime der Marke bei 32 Prozent. */
  --app-state-cta-hover-opacity: 0.92; /** Deckkraft der Hauptaktion unter dem Zeiger. Der Wert stammt aus dem Kundenportal (.dsa-cta:hover opacity 0.92) -- uebernommen statt neu gewaehlt, damit die Hauptaktion in beiden Flaechen gleich reagiert. */
  --app-layer-base: 0; /** Page content. */
  --app-layer-sticky: 40; /** Sticky header, sticky table head. */
  --app-layer-drawer: 50; /** Side drawer, bottom sheet. */
  --app-layer-overlay: 60; /** Modal backdrop. */
  --app-layer-modal: 70; /** Modal panel. */
  --app-layer-toast: 80; /** Toasts and alerts, above a modal so an error inside one is readable. */
  --app-layer-tooltip: 90; /** Tooltips, topmost. */
  --app-motion-duration-fast: 150ms; /** State changes: hover, focus, selection. Observed in the live customer portal. */
  --app-motion-duration: 300ms; /** Entrances and exits. Observed in the brand's own .btn transition. */
  --app-motion-reduced: 0ms; /** Under prefers-reduced-motion. The brand says nothing about this; an app must. */
  --app-primitive-control-height: 40px; /** Button and input height. Observed in both existing DSA apps. */
  --app-primitive-control-height-sm: 32px; /** Dense toolbars, inline actions. */
  --app-primitive-control-height-touch: 44px; /** Minimum touch target on phones. No core token has this value. */
  --app-primitive-table-row-height: 44px; /** Scannable without crowding. */
  --app-primitive-hover-tint: 0.08; /** Deckkraft der Flaeche unter dem Zeiger. 0.08 ist der Wert, den das Crew-Frontend als hover:bg-white/8 an den meisten Stellen benutzt — gemessen 2026-10-08, zusammen mit /4 und /2 an einzelnen Stellen. Ein Wert statt drei, damit nicht wieder jede App ihren eigenen waehlt. */
  --app-primitive-nav-brand-height: 28px; /** Hoehe des Markenzeichens im Kopf der Navigation. Das Crew-Frontend rendert das Vollzeichen mit h-7, also 28px -- derselbe Wert, damit nicht wieder jede App einen eigenen waehlt. Die Markenvorgabe (mindestens 120px Breite) bleibt eingehalten: das Zeichen ist 160x32, auf 28px Hoehe also 140px breit. */
  --app-primitive-nav-icon-size: 18px; /** Symbolgroesse in der Navigation. Am 2026-10-07 gemessen, weil die Symbole in den drei Apps sichtbar verschieden gross waren: Kundenportal 18px in der unteren Leiste, Crew-App 16px, Supplier-Testsystem 1em -- nach der Umstellung auf die Handy-Schriftstufe also 10px. Drei Flaechen, drei Werte, weil es dazu KEIN Token gab und das Designsystem zu svg nichts sagte. 18px ist der Wert des Kundenportals: dieselbe Komponente im selben Kontext. Wichtig in der Variante, in der nur der aktive Eintrag seinen Namen zeigt -- dort traegt das Symbol die Bedeutung allein. */
  --app-primitive-nav-sidebar-width: 244px; /** The live customer portal uses 244px, the crew app 240px. One value, the wider of the two. */
  --app-primitive-nav-header-height: 56px; /** Thin header above the content. 56px also exists in core as h2 desktop font size — numerically equal, semantically unrelated, so not referenced. */
  --app-primitive-breakpoint-phone: 767px; /** At or below: tables become card lists, bottom navigation replaces the sidebar. */
  --app-primitive-breakpoint-tablet: 1023px; /** At or below: sidebar collapses to icons. */
  --app-primitive-micro-space: 2px; /** 2px. Owned, not referenced, for a reason worth recording: core HAS 2px, as spacing."0.5" — but Style Dictionary reads a dot in an alias as a path separator, so (spacing.0.5) resolves to spacing -> 0 -> 5 and fails. The same is true of spacing."1.5" (6px). Those two core tokens are unreferenceable by any alias, which is a finding about core, not about this layer. */
  --app-primitive-card-min-width: 208px; /** Minimum width of a card in the card grid, below which the grid breaks to fewer columns. Owned here because core has no layout widths at all -- its only width is container.max 1304px, which is the marketing page. 208px holds a six-digit currency value plus its label without wrapping, measured at the app body size of 16px. Added because the dimension guard in 09-components caught a bare 13rem in the grid rule -- the first time that guard fired in normal use rather than in its own test. */
  --app-disabled-opacity: 0.48; /** Opacity of a disabled control. Owned here because core has no opacity scale at all -- the crew app invented its own ladder (4/8/16/24/32/48/64/80) and the brand has only the white and black alpha rungs, which are colours, not opacities. 0.48 matches the rung the brand uses for muted text, so a disabled control reads as the same degree of recessed. Without this token the component library would carry a bare 0.48, and a bare number is how a token system starts leaking. */
  --web-density: comfortable; /** Marketing density. Generous whitespace is a stated brand rule: sections use 120-160px desktop padding, content breathes. */
  --web-breakpoint-mobile: 1199px; /** Where typography and section spacing switch to the mobile column. NOTE: buttons, the motto badge and the container gutter switch at 991px instead. The two limits are inconsistent today; this file records the inconsistency rather than resolving it, because resolving it would change the live site. */
  --radius-button: var(--radius-md); /** Schaltflaechen und schaltflaechenaehnliche Bedienelemente (Navigationseintraege). 8px. */
  --radius-card: var(--radius-xl); /** Karten, Tafeln, Bilder. 16px. */
  --radius-input: var(--radius-md); /** Eingabefelder. 8px, wie .dsa-ds-input es heute setzt. Soll sich das aendern, aendert sich dieser Alias -- an einer Stelle, nicht in jeder Regel. */
  --radius-pill: var(--radius-full); /** Pillen, Punkte, Avatare -- alles, was vollstaendig rund ist. */
  --app-text-body: var(--font-text-style-body-sm-font-size-desktop); /** App reading size. Marketing body is 20px; at 20px a data table needs horizontal scrolling on a laptop. */
  --app-text-body-sm: var(--font-text-style-body-sm-font-size-mobile); /** Table cells, secondary labels. Matches core typography.body-sm desktop. */
  --app-text-label: var(--font-text-style-body-xs-font-size-desktop); /** Uppercase column heads and field labels. Matches core typography.body-xs desktop. */
  --app-text-label-mobile: var(--font-text-style-body-xs-font-size-mobile); /** Dieselbe Rolle auf dem Telefon. Die Marke definiert body-xs ZWEIMAL -- 12px Desktop, 10px Handy -- und das Designsystem nahm auf dem Telefon die Desktop-Stufe. In der unteren Navigationsleiste kostet das echten Platz: bei 360px Bildschirm ist ein Eintrag 63px breit, und "Performance" braucht in 12px 73px, in 10px rund 61px. Gemessen 2026-10-07 im Supplier-Testsystem. */
  --app-text-page-title: var(--font-text-style-h4-font-size-desktop); /** Largest size an app page uses. Marketing h1 is 72px; a working surface does not open with a billboard. */
  --app-control-height: var(--app-primitive-control-height); /** Button and input height. Observed in both existing DSA apps. */
  --app-control-height-sm: var(--app-primitive-control-height-sm); /** Dense toolbars, inline actions. */
  --app-control-height-touch: var(--app-primitive-control-height-touch); /** Minimum touch target on phones. */
  --app-table-row-height: var(--app-primitive-table-row-height); /** Scannable without crowding. */
  --app-table-cell-padding-y: var(--spacing-2); /** 8px. The live customer portal uses 10px (py-2.5), which has no core token — the 4px scale has no 2.5 step. Snapping to 8px is the point of a token system; an off-scale value is how a scale quietly stops being one. */
  --app-table-cell-padding-x: var(--spacing-4); /** 16px. */
  --app-card-padding: var(--spacing-5); /** 20px. Marketing cards use 32px; a dashboard with 32px cards fits three KPIs where it needs six. */
  --app-section-spacing: var(--spacing-8); /** 32px between blocks on a page, against 160px on the site. */
  --app-pill-radius: var(--radius-md); /** 8px. NOT radius.full: the brand's only round badge is .motto, an uppercase lime marketing label. A status pill must not look like a motto. */
  --app-pill-padding-y: var(--app-primitive-micro-space); /** 2px. */
  --app-pill-padding-x: var(--spacing-2); /** 8px. */
  --app-state-hover-tint: var(--app-primitive-hover-tint); /** Die eine Deckkraft fuer Flaechen unter dem Zeiger. */
  --app-nav-brand-height: var(--app-primitive-nav-brand-height); /** Die eine Hoehe des Markenzeichens. */
  --app-nav-icon-size: var(--app-primitive-nav-icon-size); /** Die eine Symbolgroesse der Navigation. Vorher hatte jede App ihre eigene. */
  --app-nav-sidebar-width: var(--app-primitive-nav-sidebar-width); /** Observed in the live customer portal; the crew app uses 240px. One value, picked as the wider of the two. */
  --app-nav-header-height: var(--app-primitive-nav-header-height); /** Thin header above the content, observed in both apps. */
  --app-breakpoint-phone: var(--app-primitive-breakpoint-phone); /** At or below: tables become card lists, bottom navigation replaces the sidebar. Observed in the live customer portal. */
  --app-breakpoint-tablet: var(--app-primitive-breakpoint-tablet); /** At or below: sidebar collapses to icons. */
  --web-text-body: var(--font-text-style-body-font-size-desktop); /** 20px desktop / 16px mobile. Reading size for prose, not for data. */
  --web-section-spacing: var(--section-spacing-xl); /** 160px desktop / 48px mobile. The marketing rhythm. */
  --web-card-padding: var(--spacing-8); /** 32px, per .card in 03-visual-identity/ui-components.md. */
  --dark-background-canvas: var(--color-background-dark-primary); /** Page ground, #0C0B16. */
  --dark-background-raised: var(--color-background-dark-secondary); /** Cards and menus, #2D2C43. */
  --dark-background-elevated: var(--color-background-dark-elevated); /** Panels above cards, #10122A. */
  --dark-text-primary: var(--color-text-title-light); /** Headings on dark. */
  --dark-text-secondary: var(--color-text-body-on-dark); /** Body on dark, 64% white. */
  --dark-text-muted: var(--color-text-muted); /** Captions and column heads, 48 percent white. THE ONE REMAINING GAP in this layer, and it is a core value rather than something proposed here. Measured: 4.99:1 on the page ground #0C0B16 but 4.36:1 on the card #2D2C43 -- short of AA by 0.14. The first alpha that clears the card is 0.50, which is not on the brand's white ladder (0.08 / 0.16 / 0.32 / 0.48 / 0.64 / 1.0), and 0.64 is already text.secondary. So there are two honest options and both are core decisions: add a rung at 0.50, or accept that the muted tier exists on the page ground and captions on a card use text.secondary. Left as measured rather than quietly rounded. */
  --dark-text-on-accent: var(--color-primary); /** Text ON the lime accent. Contrast belongs in output/a11y.json. */
  --dark-border-default: var(--color-border-light); /** Rules and outlines on dark, 16% white. */
  --dark-border-subtle: var(--color-white-8); /** Table row separators, 8% white. */
  --dark-action-primary: var(--color-secondary); /** Lime. Core restricts it to CTAs, highlights and interactive elements — which is why it must NOT also be the success colour. */
  --dark-action-primary-hover: var(--color-secondary-hover); /** Zeiger ueber der Hauptschaltflaeche. Die Marke hat diesen Wert (helleres Lime) seit jeher — er war nur nie an eine Rolle gebunden, und deshalb hatte .dsa-ds-button bis 2026-10-08 ueberhaupt keinen Hover: wohl einen transition fuer background-color, aber keine Regel, die etwas aendert. */
  --dark-focus-ring: var(--color-secondary); /** Focus ring. Core removes the ring entirely (.form-input:focus sets outline to none) — defensible on a landing page, a keyboard trap in an app. The live customer portal already uses lime at 2px with a 2px offset; this ratifies that. */
  --dark-status-danger: var(--color-status-danger-on-dark); /** Errors and destructive actions. 6.52:1 on canvas, 4.51:1 on the card. */
  --dark-status-warning: var(--color-status-warning-on-dark); /** Warnings. 9.95:1 / 6.89:1. */
  --dark-status-success: var(--color-status-success-on-dark); /** Completed, paid, confirmed. 8.02:1 / 5.55:1. NOT lime -- see the raw token description. */
  --dark-status-info: var(--color-status-info-on-dark); /** Informational. 6.51:1 / 4.51:1. */
  --light-background-canvas: var(--color-background-light); /** Page ground, #E2E3E7. */
  --light-background-raised: var(--color-background-grey); /** Cards, #F3EDE9. */
  --light-text-primary: var(--color-text-title-dark); /** Headings on light. */
  --light-text-secondary: var(--color-text-body-on-light); /** Body on light, 64% ink. */
  --light-text-on-accent: var(--color-primary); /** Text ON the lime accent. */
  --light-border-default: var(--color-border-dark); /** Rules on light, 16% ink. */
  --light-action-primary: var(--color-secondary); /** Lime, same role, same raw value. */
  --light-action-primary-hover: var(--color-secondary-hover); /** Gleiche Rolle wie im dunklen Thema. Ohne den Eintrag HIER faellt die helle Oberflaeche auf eine Variable zurueck, die es nicht gibt, und die Schaltflaeche bliebe beim Zeigen unveraendert -- der Fehler waere nur im hellen Thema sichtbar und damit leicht zu uebersehen. */
  --light-focus-ring: var(--color-utility-blue); /** On a light ground lime measures 1.00:1 and is invisible, so the ring cannot be lime here. utility.blue measures 6.45:1 on #F3EDE9 — but core restricts it to special accents, so this needs the same decision as info. */
  --light-status-danger: var(--color-status-danger-on-light); /** Errors and destructive actions. 4.52:1 on canvas, 4.99:1 on the card. */
  --light-status-warning: var(--color-status-warning-on-light); /** Warnings. 4.61:1 / 5.10:1. */
  --light-status-success: var(--color-status-success-on-light); /** Completed, paid, confirmed. 4.94:1 / 5.46:1. */
  --light-status-info: var(--color-utility-blue); /** Informational. utility.blue works unchanged on light: 5.84:1 / 6.45:1. */
  --app-pill-text: var(--dark-text-primary); /** Pill TEXT is the primary text colour of its mode, not the role colour. Measured on the 16 percent fill: 10.94:1 danger, 9.82:1 warning, 10.30:1 success, 10.59:1 info on dark; 12.71 / 13.61 / 13.47 / 13.10 on light. All pass AA with margin. On a light surface use light.text.primary -- this alias names the dark case because dark is the primary surface. */
}

/* DropshipAgent Design System — Komponenten-Regeln.
 *
 * Hier steht KEIN :root-Block mit Werten. Die Werte kommen aus 06-design-tokens
 * und werden beim Bau vorangestellt (scripts/build-css.mjs). Ein Farbliteral in
 * dieser Datei bricht den Bau — das ist der einzige Grund, warum die Tokens die
 * Wahrheit bleiben und nicht nur die Absicht sind.
 *
 * Klassenpraefix ist `dsa-ds-`, nicht `dsa-`. Das DSA Customer Portal hat
 * bereits globale Klassen .dsa-card, .dsa-btn, .dsa-pill, .dsa-cta, .dsa-navlink
 * und .dsa-kpi in seiner globals.css. Laden beide Stylesheets, gewinnt die
 * spaetere — ein Fehler, den niemand im Code sieht, nur auf dem Bildschirm.
 *
 * Die Regeln stehen in :where(.dsa-ds-root) und damit auf Spezifitaet 0,0,1.
 * Mit `.dsa-ds-root a` (0,1,1) schlaegt die Linkfarbe jede Komponentenklasse
 * (0,1,0) -- in der Referenzmarke wurde so die Primaerschaltflaeche lime auf
 * lime und ihr Text verschwand. Kein Typecheck findet das, nur das Rendern.
 */

/* --- Die Schrift ---------------------------------------------------------- *
 * Bis 2026-10-07 setzte die Grundlage unten `font-family: var(--font-family-primary)`,
 * was auf 'TWK Everett' aufloest -- und das Paket lieferte keine einzige Schriftdatei
 * und keine @font-face. Jeder Konsument fiel still auf die Systemschrift zurueck.
 * Bedingung 2 des Vertrags lautet: "Eine Komponente ohne ihre Werte rendert stumm
 * falsch." Genau das ist passiert, acht Wochen lang unbemerkt, weil im Code alles
 * richtig aussah und nur der Bildschirm etwas anderes zeigte.
 *
 * Die Dateien liegen seit jeher in 04-assets/typography/webfonts/; der Bau kopiert
 * sie jetzt nach dist/fonts/ und bricht ab, wenn eine hier genannte Datei dort
 * fehlt. Deklaration und Auslieferung koennen damit nicht mehr auseinanderlaufen.
 *
 * Nur woff2, und nur Light und Medium: das sind die Schnitte, die die Marke fuehrt.
 * font-display:swap -- ein kurzer Umbruch ist besser als leerer Text. */
@font-face { font-family: 'TWK Everett'; src: url('./fonts/TWKEverett-Light.woff2') format('woff2');
             font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: 'TWK Everett'; src: url('./fonts/TWKEverett-LightItalic.woff2') format('woff2');
             font-weight: 300; font-style: italic; font-display: swap; }
@font-face { font-family: 'TWK Everett'; src: url('./fonts/TWKEverett-Medium.woff2') format('woff2');
             font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'TWK Everett'; src: url('./fonts/TWKEverett-MediumItalic.woff2') format('woff2');
             font-weight: 500; font-style: italic; font-display: swap; }

/* --- Rollen-Aliasse ------------------------------------------------------- *
 * Die Marke fuehrt ihre Rollen zweimal: --dark-* und --light-*. Die Komponenten
 * brauchen EINEN Namen je Rolle, sonst verdoppelt jeder Moduswechsel jede Regel.
 * Dunkel ist die Vorgabe, weil die Marke dark-first ist ("Scheme A: Dark
 * Sections (Primary)").                                                       */
.dsa-ds-root {
  --ds-bg: var(--dark-background-canvas);
  /* Die Marke fuehrt fuer die Seitenflaeche einen eigenen Verlauf. Er ist nur fuer
     Dunkel definiert, deshalb ein Alias je Modus statt einer modusfreien Regel. */
  --ds-surface: var(--color-gradient-dark-surface);
  --ds-raised: var(--dark-background-raised);
  --ds-elevated: var(--dark-background-elevated);
  --ds-text: var(--dark-text-primary);
  --ds-text-2: var(--dark-text-secondary);
  /* Absichtlich UNBENUTZT in den Komponenten-Regeln, und das ist eine Entscheidung,
     kein Versehen: dark.text.muted misst 4,99:1 auf dem Seitengrund und 4,36:1 auf der
     Karte. Eine Bibliothek weiss nicht, auf welcher Flaeche ihr Bauteil landet, also
     darf sie die Rolle nicht benutzen, solange sie nur auf einer von zwei Flaechen
     traegt. roles.json haelt es fest: Captions auf einer Karte nehmen text.secondary.
     Der Alias bleibt stehen, damit die Zuordnung sichtbar ist -- er wird benutzbar,
     sobald der Kern die Sprosse bei 0,50 bekommt. Gefunden von codex review. */
  --ds-text-3: var(--dark-text-muted);
  --ds-on-accent: var(--dark-text-on-accent);
  --ds-border: var(--dark-border-default);
  --ds-border-subtle: var(--dark-border-subtle);
  --ds-accent: var(--dark-action-primary);
  --ds-accent-hover: var(--dark-action-primary-hover);
  /* Schrift auf einer SCHWACH getoenten Akzentflaeche (16 %). Auf dunkel traegt der Akzent
     selbst (11,44:1) -- genau so macht es das Kundenportal. Auf hell scheitert er (1,09:1),
     deshalb dort text.primary. Eine Rolle statt einer Ausnahme. */
  --ds-on-accent-subtle: var(--dark-action-primary);
  --ds-focus: var(--dark-focus-ring);
  --ds-danger: var(--dark-status-danger);
  --ds-warning: var(--dark-status-warning);
  --ds-success: var(--dark-status-success);
  --ds-info: var(--dark-status-info);
  /* Die beiden Verlaeufe der Marke. Sie standen im Kundenportal HARTKODIERT in globals.css
     (--grad-signature, --grad-hero-cta), zeichengleich mit den Marken-Tokens -- also einmal
     definiert und einmal abgeschrieben. Hier bekommen sie eine Rolle. */
  --ds-signature: var(--color-gradient-signature);
  --ds-cta: var(--color-gradient-hero-cta);
}
.dsa-ds-root.dsa-ds-light {
  --ds-bg: var(--light-background-canvas);
  --ds-surface: var(--light-background-canvas);   /* kein heller Verlauf in der Marke */
  --ds-raised: var(--light-background-raised);
  --ds-elevated: var(--light-background-raised);
  --ds-text: var(--light-text-primary);
  --ds-text-2: var(--light-text-secondary);
  /* Kein muted auf hell: der erste tragfaehige Alphawert ist 0,60 und
     text.secondary ist 0,64 -- eine Rolle 0,04 daneben ist keine Rolle.
     Captions nehmen hier text.secondary, so steht es in semantic/roles.json. */
  --ds-text-3: var(--light-text-secondary);
  --ds-on-accent: var(--light-text-on-accent);
  --ds-border: var(--light-border-default);
  --ds-border-subtle: var(--light-border-default);
  --ds-accent: var(--light-action-primary);
  --ds-on-accent-subtle: var(--light-text-primary);
  --ds-accent-hover: var(--light-action-primary-hover);
  --ds-focus: var(--light-focus-ring);
  --ds-danger: var(--light-status-danger);
  --ds-warning: var(--light-status-warning);
  --ds-success: var(--light-status-success);
  --ds-info: var(--light-status-info);
  /* Die beiden Verlaeufe der Marke. Sie standen im Kundenportal HARTKODIERT in globals.css
     (--grad-signature, --grad-hero-cta), zeichengleich mit den Marken-Tokens -- also einmal
     definiert und einmal abgeschrieben. Hier bekommen sie eine Rolle. */
  --ds-signature: var(--color-gradient-signature);
  --ds-cta: var(--color-gradient-hero-cta);
}

/* --- Grundlage ----------------------------------------------------------- */
.dsa-ds-root {
  font-family: var(--font-family-primary);
  font-size: var(--app-text-body);
  line-height: var(--font-line-height-relaxed);
  letter-spacing: var(--font-text-style-body-letter-spacing);
  color: var(--ds-text);
  /* Kein background-attachment: fixed. Es sah auf dem Desktop ruhiger aus, aber iOS
     Safari setzt es nicht um und im Druck wiederholt sich der Verlauf je Seite -- eine
     Eigenschaft, die auf vier Oberflaechen unterschiedlich wirkt, ist keine gute Grundlage. */
  background: var(--ds-surface);
  -webkit-font-smoothing: antialiased;
}
:where(.dsa-ds-root) *, :where(.dsa-ds-root) *::before, :where(.dsa-ds-root) *::after { box-sizing: border-box; }
:where(.dsa-ds-root) h1 { font-size: var(--app-text-page-title); font-weight: var(--font-weight-medium); line-height: var(--font-line-height-tight); margin: 0 0 var(--spacing-4); }
:where(.dsa-ds-root) h2 { font-size: var(--font-text-style-h6-font-size-desktop); font-weight: var(--font-weight-medium); margin: 0 0 var(--spacing-3); }
:where(.dsa-ds-root) p { color: var(--ds-text-2); margin: 0 0 var(--spacing-3); }
/* Links erben die Textfarbe und tragen eine Unterstreichung.
 *
 * Hier stand `color: var(--ds-accent)` -- Lime, und auf dem hellen Grund gibt das
 * rund 1:1, der Link war unsichtbar. Dazu nahm `text-decoration: none` das einzige
 * Merkmal weg, das nicht Farbe ist. Eine Unterstreichung traegt in beiden Modi und
 * braucht keine zweite Farbe. Gefunden von codex review. */
:where(.dsa-ds-root) a { color: inherit; text-decoration: underline; text-underline-offset: 0.2em; }
:where(.dsa-ds-root) a:hover { text-decoration-thickness: var(--app-primitive-micro-space); }
/* Bauteile, die selbst ein Aussehen mitbringen, nehmen die Unterstreichung zurueck. */
:where(.dsa-ds-root) a.dsa-ds-nav-item, :where(.dsa-ds-root) a.dsa-ds-button { text-decoration: none; }

/* --- Button -------------------------------------------------------------- *
 * Lime traegt nur der Primaerknopf. Die Marke sagt dazu, der Akzent sei
 * "used exclusively for CTAs, highlights, and interactive elements" -- genau
 * deshalb ist die Erfolgsfarbe eine andere und nicht ebenfalls lime.          */
.dsa-ds-button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--spacing-1);
  height: var(--app-control-height);
  padding: 0 var(--spacing-4);
  border: var(--spacing-px) solid transparent;
  border-radius: var(--radius-button);
  font-family: inherit;
  font-size: var(--font-text-style-button-font-size-desktop);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--font-text-style-button-letter-spacing);
  cursor: pointer;
  transition: background-color var(--app-motion-duration-fast), border-color var(--app-motion-duration-fast), color var(--app-motion-duration-fast);
}
/* Zeiger ueber der Schaltflaeche.
 *
 * Bis 2026-10-08 gab es das NICHT -- in keiner der vier Varianten. Der `transition` eine
 * Regel weiter oben stand trotzdem da und animierte eine Aenderung, die nie stattfand. Das
 * ist der Fingerabdruck eines Hovers, der geplant und nie geschrieben wurde. Thomas am
 * 2026-10-07: "in den anderen Apps haben wir bestimmte Button-Hover-Effekte, die sind hier
 * anders und schlechter."
 *
 * Gemessen, woran sich das orientiert: das Crew-Frontend benutzt `hover:bg-white/8` an den
 * meisten Stellen (dazu /4 und /2), `hover:text-white` und `hover:border-white/32`. Daraus
 * ist EIN Wert geworden, --app-state-hover-tint, damit nicht wieder jede App ihren eigenen
 * waehlt.
 *
 * `:not([disabled])` ist nicht kosmetisch: eine abgeschaltete Schaltflaeche, die auf den
 * Zeiger reagiert, behauptet, sie sei bedienbar. Im Supplier-Portal sind genau zwei
 * Schaltflaechen abgeschaltet, und beide wuerden sonst mitleuchten. */
.dsa-ds-button-primary:not([disabled]):hover { background: var(--ds-accent-hover); }
/* Der ruhige Rand WAERMT ZU LIME. Die Flaeche bleibt, wie sie ist.
 *
 * Mein erster Versuch toente die Flaeche (nach dem Vorbild des Crew-Frontends,
 * hover:bg-white/8). Thomas am 2026-10-08: "Button Hover does not match." Richtig -- das
 * Kundenportal macht es anders herum, und dessen Kommentar sagt auch warum: "Secondary
 * action -- quiet bordered button that warms to lime".
 *
 *   .dsa-btn:not(:disabled):hover { border-color: var(--clr-green); color: var(--txt); }
 *
 * Keine Flaechenaenderung, sondern Rand in der Akzentfarbe und hellerer Text. Das ist der
 * Unterschied zwischen "etwas passiert hier" und "das hier ist anklickbar": die geleuchtete
 * Kante zeigt die Grenze des Ziels, eine getoente Flaeche verwischt sie. */
.dsa-ds-button-secondary:not([disabled]):hover {
  border-color: var(--ds-accent);
  color: var(--ds-text);
}
.dsa-ds-button-ghost:not([disabled]):hover {
  background: color-mix(in srgb, var(--ds-text) calc(var(--app-state-hover-tint) * 100%), transparent);
  color: var(--ds-text);
}
.dsa-ds-button-danger:not([disabled]):hover {
  background: color-mix(in srgb, var(--ds-danger) calc(var(--app-state-hover-tint) * 100%), transparent);
}
.dsa-ds-button:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); }
.dsa-ds-button[disabled] { opacity: var(--app-disabled-opacity); cursor: not-allowed; }
.dsa-ds-button-sm { height: var(--app-control-height-sm); padding: 0 var(--spacing-3); font-size: var(--font-text-style-button-small-font-size-desktop); }
.dsa-ds-button-primary { background: var(--ds-accent); color: var(--ds-on-accent); }
.dsa-ds-button-secondary { background: transparent; color: var(--ds-text); border-color: var(--ds-border); }
.dsa-ds-button-ghost { background: transparent; color: var(--ds-text-2); }
.dsa-ds-button-danger { background: transparent; color: var(--ds-danger); border-color: var(--ds-danger); }


/* --- Wahl aus wenigen Moeglichkeiten --------------------------------------
 *
 * Sprache, Ansicht, Zeitraum: eine Handvoll sich ausschliessender Moeglichkeiten. EIN
 * Bauteil fuer beide Zustaende, der Zustand steckt in aria-selected.
 *
 * Vorher baute das Supplier-Portal das aus ZWEI Bauteilen: die aktive Moeglichkeit war eine
 * Pille, die uebrigen waren Schaltflaechen. Die Pille ist dort das Bauteil fuer Status
 * ("Tracking Pending", "CONFIRMED") -- sie fuer "ausgewaehlt" zu benutzen, vermischt zwei
 * Bedeutungen, und ein Steuerelement aus zwei Bauteilen sieht aus wie zwei Dinge statt wie
 * einer Wahl. Thomas am 2026-10-07: "Language-Setting sieht hier auch anders aus als in der
 * App, die wir gebaut haben."
 *
 * Die Form folgt dem Kundenportal (performance-hub.tsx, branding-tabs.tsx). */
.dsa-ds-choice { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-2); margin: 0 0 var(--spacing-2); }
.dsa-ds-choice-label {
  font-size: var(--app-text-label); text-transform: uppercase;
  letter-spacing: var(--font-text-style-motto-letter-spacing); color: var(--ds-text-2);
}
.dsa-ds-choice-set { display: flex; flex-wrap: wrap; gap: var(--spacing-2); }
/* Rund, nicht eckig. Eine Wahl aus wenigen Moeglichkeiten sieht im Kundenportal anders aus
 * als eine Schaltflaeche: list-toolbar.tsx gibt den Segmenten `rounded-full`, waehrend
 * .dsa-btn bei --radius-button bleibt. Die Form unterscheidet "waehlen" von "ausloesen". */
.dsa-ds-choice-set .dsa-ds-button { border-radius: var(--radius-pill); }

/* --- Markenzeichen im Kopf der Navigation --------------------------------
 * Vorgabe aus dem Brandportal: auf dunklem Grund "Green Icon + White Text", mindestens
 * 120 px breit. Die Hoehe traegt --app-nav-brand-height, damit nicht jede App eine eigene
 * waehlt -- genau das war bei der Symbolgroesse passiert (18/16/10 px auf drei Flaechen). */
.dsa-ds-nav-brand { display: flex; flex-direction: column; gap: var(--spacing-1); margin: 0 0 var(--spacing-4); }
.dsa-ds-nav-brand img { height: var(--app-nav-brand-height); width: auto; }
.dsa-ds-nav-brand span {
  font-size: var(--app-text-label); text-transform: uppercase;
  letter-spacing: var(--font-text-style-motto-letter-spacing); color: var(--ds-text-2);
}

/* --- Seitenkopf, Auftakt, Aktionsliste ------------------------------------
 *
 * Portiert aus dem Kundenportal, nachdem Thomas am 2026-10-08 sagte: "das ist wirklich das
 * beste Design". Nachgelesen wurde dort der Quelltext, nicht der Bildschirm --
 * components/shell/header.tsx, app/(app)/page.tsx und app/globals.css.
 *
 * Was diese Seite besser macht als eine Reihe gleich grosser Kennzahlkarten, in der
 * Reihenfolge ihrer Wirkung:
 *   1. Ein Seitenkopf trennt "wo bin ich und was kann ich tun" vom Inhalt. Das Konto und die
 *      Hauptaktion stehen oben RECHTS, nicht in der Seitenleiste.
 *   2. Ein Auftakt spricht den Leser an ("Your store today" / "Welcome, Aron.") statt mit
 *      einer Ueberschrift zu beginnen, die nur den Menuepunkt wiederholt.
 *   3. EINE Zahl ist laut, der Rest ist leise. Die Karte hat einen Hauptwert und darunter
 *      kleinere Zahlen -- nicht vier gleich laute Karten nebeneinander.
 *   4. Die Farbe traegt Bedeutung: Guthaben gruen, Ueberfaelliges rot, alles andere ruhig.
 *   5. Eine Aktionsliste sagt, was zu tun ist. Eine Kennzahl sagt nur, wie es steht. */

/* Kopfzeile der Seite: Titel links, Konto und Hauptaktion rechts. */
.dsa-ds-topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--spacing-4); flex-wrap: wrap;
  padding: 0 0 var(--spacing-5);
  border-bottom: var(--spacing-px) solid var(--ds-border-subtle);
  /* Der Abstand kommt vom Abstand der Hauptflaeche, nicht zusaetzlich: --app-section-spacing
     unter einer Kopfzeile, die ohnehin schon einen Trennstrich hat, riss eine Luecke auf. */
  margin: 0 0 var(--spacing-2);
}
.dsa-ds-topbar-title { display: flex; align-items: center; gap: var(--spacing-3); min-width: 0; }
.dsa-ds-topbar-title h1 { margin: 0; font-size: var(--app-text-page-title); font-weight: var(--font-weight-medium); }
.dsa-ds-topbar-actions { display: flex; align-items: center; gap: var(--spacing-3); flex-wrap: wrap; }

/* Der Signaturpunkt. Das Zeichen der Marke in klein -- im Kundenportal steht er vor jedem
 * Seitentitel. */
.dsa-ds-dot {
  display: inline-block; width: var(--spacing-2); height: var(--spacing-2);
  border-radius: var(--radius-pill); background: var(--ds-signature); flex: none;
}

/* Kleine Zeile ueber einer Ueberschrift, in der Akzentfarbe. */
.dsa-ds-eyebrow {
  font-size: var(--app-text-label); font-weight: var(--font-weight-medium);
  text-transform: uppercase; letter-spacing: var(--font-text-style-motto-letter-spacing);
  color: var(--ds-accent); margin: 0 0 var(--spacing-1);
}
/* Kurzer Strich unter dem Auftakt, im Signaturverlauf. */
.dsa-ds-accent-rule {
  height: var(--app-primitive-micro-space); width: var(--spacing-12);
  border: 0; border-radius: var(--radius-pill);
  background: var(--ds-signature); margin: var(--spacing-3) 0 0;
}

/* Hauptaktion. Der Verlauf der Marke, dunkle Schrift darauf. */
.dsa-ds-cta {
  display: inline-flex; align-items: center; gap: var(--spacing-1-5);
  height: var(--app-control-height); padding: 0 var(--spacing-4);
  border: 0; border-radius: var(--radius-button);
  font-family: inherit; font-size: var(--font-text-style-button-small-font-size-desktop);
  font-weight: var(--font-weight-medium); letter-spacing: var(--font-text-style-button-letter-spacing);
  text-transform: uppercase; text-decoration: none; cursor: pointer; white-space: nowrap;
  background: var(--ds-cta); color: var(--ds-on-accent);
  transition: opacity var(--app-motion-duration-fast), box-shadow var(--app-motion-duration-fast);
}
/* Deckkraft UND Leuchten. Das Leuchten hatte ich beim ersten Portieren weggelassen und
 * damit genau das weggelassen, was man sieht. Kundenportal:
 *   .dsa-cta:not(:disabled):hover { opacity: 0.92; box-shadow: 0 0 24px var(--clr-green-32); } */
.dsa-ds-cta:not([disabled]):hover {
  opacity: var(--app-state-cta-hover-opacity);
  box-shadow: 0 0 var(--spacing-6) color-mix(in srgb, var(--ds-accent) calc(var(--app-state-cta-glow-tint) * 100%), transparent);
}
.dsa-ds-cta:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); }

/* Zweitwerte unter dem Hauptwert einer Karte: zwei Spalten, abgetrennt. */
.dsa-ds-kpi-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-3); margin: var(--spacing-3) 0 0;
  padding: var(--spacing-3) 0 0; border-top: var(--spacing-px) solid var(--ds-border-subtle);
}
.dsa-ds-kpi-grid dt { font-size: var(--app-text-label); color: var(--ds-text-2); margin: 0; }
.dsa-ds-kpi-grid dd { margin: 0; font-weight: var(--font-weight-medium); }
.dsa-ds-kpi-grid dd small { display: block; font-size: var(--app-text-label); font-weight: var(--font-weight-light); color: var(--ds-text-2); }

/* Breite Karte neben schmaler: ein Drittel, zwei Drittel. Unter Tabletbreite untereinander. */
.dsa-ds-split { display: grid; grid-template-columns: 1fr; gap: var(--app-section-spacing); }

/* Aktionsliste: was zu tun ist, nicht wie es steht. */
/* Fuss der Seite: leise, abgesetzt, am Ende. */
.dsa-ds-footer {
  margin: var(--app-section-spacing) 0 0;
  padding: var(--spacing-4) 0 0;
  border-top: var(--spacing-px) solid var(--ds-border-subtle);
  font-size: var(--app-text-label); color: var(--ds-text-2);
}
.dsa-ds-queue { list-style: none; margin: 0; padding: 0; }
.dsa-ds-queue-item {
  display: flex; align-items: center; gap: var(--spacing-3);
  padding: var(--spacing-3) 0; border-bottom: var(--spacing-px) solid var(--ds-border-subtle);
  text-decoration: none; color: inherit;
}
.dsa-ds-queue-item:last-child { border-bottom: 0; }
.dsa-ds-queue-item:hover { background: color-mix(in srgb, var(--ds-text) calc(var(--app-state-hover-tint) * 100%), transparent); }
.dsa-ds-queue-item:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); }
.dsa-ds-queue-text { flex: 1 1 auto; min-width: 0; }
.dsa-ds-queue-text strong { display: block; font-weight: var(--font-weight-medium); }
.dsa-ds-queue-text small { color: var(--ds-text-2); }
.dsa-ds-queue-mark { width: var(--spacing-2); height: var(--spacing-2); border-radius: var(--radius-pill); flex: none; }
.dsa-ds-queue-leer { color: var(--ds-text-2); margin: 0; }

/* --- Card ---------------------------------------------------------------- */
.dsa-ds-card {
  background: var(--ds-raised);
  border: var(--spacing-px) solid var(--ds-border-subtle);
  border-radius: var(--radius-card);
  padding: var(--app-card-padding);
}
/* Karten nebeneinander. Ein echtes Muster, nicht erfunden: alle vier DSA-Oberflaechen
 * zeigen Kennzahlenkarten in einer Reihe, und die Referenzmarke hat dafuer CardGrid.
 * minmax mit auto-fit statt einer festen Spaltenzahl -- so bricht es von selbst um,
 * ohne eine zweite Media Query. */
.dsa-ds-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--app-primitive-card-min-width), 1fr)); gap: var(--app-section-spacing); }

/* Eine Kennzahl, die eine offene Aufgabe benennt, soll zu der Aufgabe fuehren.
 * Thomas am 2026-10-07 zu "NOT CONFIRMED": "waere toll, wenn das ein Button waere, der sich
 * oeffnen laesst, wo die Non-Confirmed drin sind, als Pre-Filter." */
/* Zwei Dinge uebereinander in einer Tabellenzelle -- eine Zahl und ihre Markierung.
 * Nebeneinander zwingen sie die ganze Spalte auf ihre gemeinsame Breite, und die Spalte
 * daneben bricht dann um. Rechtsbuendig, weil es in der Zahlenspalte steht. */
.dsa-ds-stack { display: inline-flex; flex-direction: column; align-items: flex-end; gap: var(--spacing-1); }
.dsa-ds-card-link { display: block; text-decoration: none; color: inherit; cursor: pointer;
  transition: border-color var(--app-motion-duration-fast), background-color var(--app-motion-duration-fast); }
.dsa-ds-card-link:hover { border-color: var(--ds-border); background: var(--ds-elevated); }
.dsa-ds-card-link:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); }
.dsa-ds-card-action { color: var(--ds-accent); }
.dsa-ds-card-title { font-size: var(--app-text-label); font-weight: var(--font-weight-medium); letter-spacing: var(--font-text-style-motto-letter-spacing); text-transform: uppercase; color: var(--ds-text-2); margin: 0 0 var(--spacing-2); }
/* Die Kennzahl ist die eine Stelle, an der eine Oberflaeche nach Produkt aussieht statt
   nach Tabelle: Displaysatz, also leichter Schnitt und engere Laufweite. */
/* Fluide Groesse statt fester h4-Groesse.
 *
 * Fest gesetzt passte "318.402,90 €" nicht in eine Karte, und der Browser brach zwischen
 * Zahl und Waehrung um -- das € stand allein in der zweiten Zeile, auf drei von vier Karten
 * der Zahlungsseite. Das Kundenportal loest das seit dem Portal-Redesign mit clamp()
 * (.dsa-kpi): "fluid size so long money values stay inside half-width cards on phones".
 *
 * Dieselbe Rechnung hier, aber ueber Tokens: untere Grenze die Handy-Lesegroesse, obere die
 * Seitenueberschrift. Die Kurzschreibweise in der App bleibt trotzdem richtig -- clamp
 * macht die Zahl kleiner, die Kurzschreibweise macht sie kuerzer. Beides zusammen haelt
 * auch einen Millionenbetrag lesbar. */
.dsa-ds-card-value { font-size: clamp(var(--app-text-body-sm), 5.5vw, var(--app-text-page-title)); font-weight: var(--font-weight-light); line-height: var(--font-line-height-snug); letter-spacing: var(--font-text-style-h3-letter-spacing); margin: 0; }
.dsa-ds-card-note { font-size: var(--app-text-body-sm); color: var(--ds-text-2); margin: var(--spacing-1) 0 0; }

/* --- Table --------------------------------------------------------------- *
 * Kein Zebra: es kaempft mit der Flaechensprache aus 8-%-Weiss. Die Trennung
 * macht eine Linie, nicht ein Wechsel.                                        */
/* Der Rahmen scrollt, nicht die Seite. tabIndex am Element macht ihn mit der
 * Tastatur erreichbar -- ein scrollbarer Bereich, den man nur mit der Maus erreicht,
 * ist fuer einen Teil der Nutzer nicht erreichbar. */
.dsa-ds-table-scroll { overflow-x: auto; max-width: 100%; }
.dsa-ds-table-scroll:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); }
.dsa-ds-table { width: 100%; border-collapse: collapse; font-size: var(--app-text-body-sm); }
.dsa-ds-table th {
  text-align: left; font-weight: var(--font-weight-light);
  font-size: var(--app-text-label); text-transform: uppercase;
  letter-spacing: var(--font-text-style-motto-letter-spacing);
  color: var(--ds-text-2);
  padding: var(--app-table-cell-padding-y) var(--app-table-cell-padding-x) var(--app-table-cell-padding-y) 0;
  border-bottom: var(--spacing-px) solid var(--ds-border-subtle);
}
.dsa-ds-table td {
  height: var(--app-table-row-height);
  padding: var(--app-table-cell-padding-y) var(--app-table-cell-padding-x) var(--app-table-cell-padding-y) 0;
  border-bottom: var(--spacing-px) solid var(--ds-border-subtle);
  color: var(--ds-text);
}
.dsa-ds-table td:last-child, .dsa-ds-table th:last-child { padding-right: 0; }
/* Element MIT Klasse (0,2,1), nicht Klasse allein (0,1,0): `.dsa-ds-table th` ist
 * 0,1,1 und schlug die Klasse, obwohl sie spaeter steht. Zahlenkoepfe blieben
 * deshalb linksbuendig, waehrend ihre Zellen rechts standen. Genau die Falle, vor
 * der der Kommentar am Dateianfang warnt -- und sie ist trotzdem passiert.
 * Gefunden von codex review, nicht vom Typecheck und nicht von der Probe. */
.dsa-ds-table th.dsa-ds-table-num,
.dsa-ds-table td.dsa-ds-table-num { text-align: right; font-variant-numeric: tabular-nums; }
.dsa-ds-table tbody tr:hover td { background: var(--ds-border-subtle); }
/* Zahl und Einheit bleiben zusammen. Ohne das bricht "1.284.903,55 €" so um, dass das €
 * allein in der zweiten Zeile steht -- auf der Uebersicht und auf Payments gesehen. Dasselbe
 * gilt fuer ein Datum und fuer einen Bezeichner: "KC-2026-" / "0451" ist keine Nummer mehr. */
.dsa-ds-table td.dsa-ds-table-num, .dsa-ds-table td.dsa-ds-nowrap, .dsa-ds-nowrap { white-space: nowrap; }
/* Statusspalte: alle Pillen gleich breit.
 * Die Spaltenbreite richtet sich nach der laengsten Beschriftung, und jede Pille fuellt sie
 * aus. Vorher war jede so breit wie ihr Wort, und eine Spalte aus verschieden breiten
 * Marken sieht ungeordnet aus -- Thomas am 2026-10-07 zur Telefonansicht: "waere schoen,
 * wenn die Status-Bits die gleiche Breite haetten, damit das nicht so weird aussieht". */
.dsa-ds-table td.dsa-ds-table-status > .dsa-ds-pill { display: flex; width: 100%; justify-content: center; }

/* --- StatusPill ---------------------------------------------------------- *
 * Der Vertrag aus output/app.json, und er weicht bewusst von beiden bestehenden
 * DSA-Apps ab: dort traegt die Rollenfarbe die SCHRIFT auf einer 16-%-Toenung
 * derselben Farbe. Gemessen gibt das 3,65:1 fuer danger und 1,68:1 fuer info,
 * und es gibt keine Toenung, bei der es traegt -- die Toenung bewegt Grund und
 * Schrift aufeinander zu. Hier traegt die Rollenfarbe Flaeche, Rand und Punkt,
 * die Schrift ist text.primary: 9,8 bis 13,6:1.                               */
.dsa-ds-pill {
  /* Eine Pille ist eine Marke, kein Absatz. Umgebrochen ("Tracking / Pending") sieht sie
   * aus wie zwei Dinge und macht die Tabellenzeile doppelt so hoch -- am 2026-10-08 im
   * Supplier-Portal nebeneinander mit "Tracking sent" zu sehen, das passte. */
  white-space: nowrap;
  display: inline-flex; align-items: center; gap: var(--spacing-1-5);
  padding: var(--app-pill-padding-y) var(--app-pill-padding-x);
  border-radius: var(--app-pill-radius);
  border: var(--spacing-px) solid currentColor;
  font-size: var(--app-text-label);
  font-weight: var(--font-weight-medium);
  /* Eigener Satz, ausdruecklich. Ohne diese Zeile uebernimmt die Pille die Laufweite
     ihres Behaelters -- in einer Kennzahlenkarte ist das -2px, und aus
     "implausible - the upstream formula is broken" wird ein einziges Wort.
     Ein Bauteil, das seinen Satz nicht festlegt, sieht je nach Einbauort anders aus. */
  letter-spacing: var(--font-text-style-body-sm-letter-spacing);
  color: var(--ds-text);
}
.dsa-ds-pill-dot { width: var(--spacing-1-5); height: var(--spacing-1-5); border-radius: var(--radius-pill); background: currentColor; flex: none; }
.dsa-ds-pill-neutral { border-color: var(--ds-border); background: var(--ds-border-subtle); }
.dsa-ds-pill-neutral .dsa-ds-pill-dot { background: var(--ds-text-2); }
.dsa-ds-pill-danger { border-color: var(--ds-danger); background: color-mix(in srgb, var(--ds-danger) calc(var(--app-pill-fill-tint) * 100%), transparent); }
.dsa-ds-pill-danger .dsa-ds-pill-dot { background: var(--ds-danger); }
.dsa-ds-pill-warning { border-color: var(--ds-warning); background: color-mix(in srgb, var(--ds-warning) calc(var(--app-pill-fill-tint) * 100%), transparent); }
.dsa-ds-pill-warning .dsa-ds-pill-dot { background: var(--ds-warning); }
.dsa-ds-pill-success { border-color: var(--ds-success); background: color-mix(in srgb, var(--ds-success) calc(var(--app-pill-fill-tint) * 100%), transparent); }
.dsa-ds-pill-success .dsa-ds-pill-dot { background: var(--ds-success); }
.dsa-ds-pill-info { border-color: var(--ds-info); background: color-mix(in srgb, var(--ds-info) calc(var(--app-pill-fill-tint) * 100%), transparent); }
.dsa-ds-pill-info .dsa-ds-pill-dot { background: var(--ds-info); }

/* --- Input --------------------------------------------------------------- *
 * Mit Rahmen und sichtbarem Fokusring. Die Marke setzt am Formularfeld
 * `outline: none` und ersetzt den Ring durch einen farbigen Unterstrich -- auf
 * einer Landingpage vertretbar, in einer Tastaturoberflaeche eine Falle.        */
.dsa-ds-field { display: block; }
.dsa-ds-label { display: block; font-size: var(--app-text-body-sm); color: var(--ds-text-2); margin-bottom: var(--spacing-1); }
.dsa-ds-input {
  display: block; width: 100%;
  height: var(--app-control-height);
  padding: 0 var(--spacing-3);
  background: transparent;
  border: var(--spacing-px) solid var(--ds-border);
  border-radius: var(--radius-input);
  font-family: inherit; font-size: var(--app-text-body); color: var(--ds-text);
}
/* Der Markenwert color.text.placeholder ist ein HELL-Wert: gemessen 6,37:1
 * auf dem hellen Grund und 2,39:1 auf dem dunklen, 1,65:1 in einer dunklen Karte.
 * Ein Platzhalter ist Text und braucht 4,5:1. Also die muted-Rolle, die in beiden
 * Modi traegt (4,99/4,36 dunkel, 5,42/5,64 hell). Gefunden von codex review. */
.dsa-ds-input::placeholder { color: var(--ds-text-2); }
.dsa-ds-input:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); border-color: var(--ds-focus); }
.dsa-ds-input[aria-invalid='true'] { border-color: var(--ds-danger); }
.dsa-ds-hint { font-size: var(--app-text-label); color: var(--ds-text-2); margin: var(--spacing-1) 0 0; }
.dsa-ds-hint-error { color: var(--ds-danger); }

/* --- Nav ----------------------------------------------------------------- */
.dsa-ds-nav {
  width: var(--app-nav-sidebar-width);
  flex: none;
  min-height: 100vh;
  border-right: var(--spacing-px) solid var(--ds-border-subtle);
  padding: var(--spacing-4);
  display: flex; flex-direction: column; gap: var(--spacing-1);
  background: var(--ds-bg);
}
.dsa-ds-nav-group { font-size: var(--app-text-label); text-transform: uppercase; letter-spacing: var(--font-text-style-motto-letter-spacing); color: var(--ds-text-2); margin: var(--spacing-4) 0 var(--spacing-1); }
.dsa-ds-nav-item {
  display: flex; align-items: center; gap: var(--spacing-2);
  padding: var(--spacing-2) var(--spacing-3);
  border-radius: var(--radius-button);
  font-size: var(--app-text-body-sm);
  color: var(--ds-text-2);
  text-decoration: none;
}
/* Die eine Symbolgroesse der Navigation.
 *
 * Bis 2026-10-07 sagte das System zu svg GAR NICHTS, und deshalb hatte jede App ihre eigene
 * Groesse: Kundenportal 18px, Crew-App 16px, Supplier-Testsystem 1em -- das sind nach der
 * Umstellung auf die Handy-Schriftstufe 10px, also halb so gross wie nebenan. Thomas dazu:
 * "Die Icons sehen anders aus." Ein Designsystem, das die Form der Leiste vorgibt, aber nicht
 * die Groesse dessen, was darin steht, gibt nur die Haelfte vor.
 *
 * `> svg` trifft beides: eine lucide-Komponente rendert ein <svg>, und ein eingebettetes
 * <svg> ist dasselbe Element. Der Konsument muss nichts umstellen.
 *
 * `flex: none` ist noetig, weil der Eintrag ein Flex-Container ist -- ohne das schrumpft das
 * Symbol auf schmalen Eintraegen mit. */
.dsa-ds-nav-item > svg {
  width: var(--app-nav-icon-size); height: var(--app-nav-icon-size); flex: none;
}
.dsa-ds-nav-item:hover { background: var(--ds-border-subtle); color: var(--ds-text); }
/* Aktiver Eintrag: getoente Akzentflaeche, Schrift in --ds-on-accent-subtle. KEINE Kante.
 *
 * Hier stand eine 2 px breite Kante links, deren Platz jeder Eintrag transparent reserviert
 * hat. Thomas am 2026-10-08: "da ist vor allem eine Art Akzentschatten links vom Hover, das
 * sieht nicht gut aus." Zwei Dinge liefen zusammen:
 *
 *   1. Die Kante folgt dem border-radius und wird an den Ecken zur Sichel -- auf dunklem
 *      Grund liest sich das als Schatten, nicht als Markierung.
 *   2. `.dsa-ds-nav-item:hover` ist (0,2,0) und `.dsa-ds-nav-item-active` war (0,1,0). Der
 *      Zeiger auf dem AKTIVEN Eintrag ueberschrieb also dessen Flaeche, und uebrig blieb die
 *      lime Kante allein auf grauem Grund. Der Eintrag sah aus wie nicht-aktiv-mit-Strich.
 *
 * Das Kundenportal hat ueberhaupt keine Kante -- nur Flaeche und Schriftfarbe:
 *   .dsa-navlink[data-active="true"] { background: var(--clr-green-16); color: var(--clr-green); }
 * Und dort gewinnt der Aktivzustand ueber den Zeiger, weil beide Regeln (0,2,0) sind und
 * die aktive zuletzt steht. Genau so hier: die Regel traegt zwei Klassen und steht NACH
 * dem Zeiger. */
.dsa-ds-nav-item.dsa-ds-nav-item-active {
  background: color-mix(in srgb, var(--ds-accent) calc(var(--app-pill-fill-tint) * 100%), transparent);
  color: var(--ds-on-accent-subtle);
  font-weight: var(--font-weight-medium);
}

/* Sichtbar fuer niemanden, lesbar fuer Vorleseprogramme.
 *
 * Diese Klasse ist die Bedingung, die der Kommentar eine Regel weiter unten seit dem
 * Review nennt: ein Zusammenklappen auf Symbole braucht verborgenen Text, nicht ein
 * ausgeblendetes Label. `display: none` und `visibility: hidden` nehmen dem Eintrag
 * seinen zugaenglichen Namen -- ein Vorleseprogramm liest dann eine leere Flaeche.
 * Das Ausschneiden laesst den Namen im Baum stehen.
 *
 * `clip-path: inset(50%)` statt des alten `clip: rect(0 0 0 0)`: letzteres ist
 * abgekuendigt und verlangt `position: absolute` ohnehin mit. */
.dsa-ds-sr-only {
  position: absolute;
  width: var(--spacing-px); height: var(--spacing-px);
  padding: 0; margin: calc(-1 * var(--spacing-px));
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.dsa-ds-nav-item:focus-visible { outline: var(--app-primitive-micro-space) solid var(--ds-focus); outline-offset: var(--app-primitive-micro-space); }
/* Auf Tabletbreite schrumpft die Leiste, die Beschriftungen bleiben.
 *
 * Hier stand `.dsa-ds-nav-item span { display: none }`. Das nimmt jedem Eintrag
 * seinen zugaenglichen Namen -- ein Eintrag ohne Symbol wird eine leere Klickflaeche,
 * und auch mit Symbol bleibt kein Name, solange der Aufrufer keinen mitgibt. Ein
 * Zusammenklappen auf Symbole braucht eine eigene Zusage am Bauteil (`iconOnly`) und
 * visuell verborgenen Text, nicht ein ausgeblendetes Label. Bis es die gibt, klappt
 * hier nichts zusammen. Gefunden von codex review. */


/* Auf Telefonbreite wird aus der Spalte eine Zeile.
 *
 * Die vorige Fassung behielt hier die Seitenleiste mit 244 px Mindestbreite. Auf
 * einem 375 px breiten Gerät blieben damit rund 67 px fuer den Inhalt, und Karten,
 * Tabelle und Knoepfe liefen aus dem Bild. Das war die Folge der Korrektur eine
 * Regel weiter oben -- ein Fix, der den naechsten Fehler erzeugt. Gefunden von
 * codex review.
 *
 * Die Beschriftungen bleiben auch hier sichtbar; die Leiste wandert nach unten,
 * wie im DSA Customer Portal. */


/* --- Layout-Huelle ------------------------------------------------------- */
/* Die Signatur: Blau -> Lime, der eine Strich, an dem man DropshipAgent erkennt.
 * Das Kundenportal traegt die Marke genau so -- ueber Schrift und diesen Verlauf,
 * nicht ueber bunte Flaechen. Ein Strich reicht; mehr waere Dekoration. */
.dsa-ds-shell { display: flex; align-items: stretch; min-height: 100vh; position: relative; }
.dsa-ds-shell::before {
  content: ''; position: absolute; inset-inline: 0; inset-block-start: 0;
  block-size: var(--spacing-0-5); background: var(--color-gradient-signature);
  /* Basisebene, nicht sticky: die Schale bildet keinen eigenen Stapelkontext, ein
     z-index:40 wuerde also im UMGEBENDEN Kontext mitspielen und dort niedrigere
     Ebenen auf den obersten zwei Pixeln uebermalen. Und ein Zierstrich faengt keine
     Klicks ab. Beides von codex review. */
  z-index: var(--app-layer-base); pointer-events: none;
}
.dsa-ds-main { flex: 1 1 auto; min-width: 0; padding: var(--app-section-spacing); display: flex; flex-direction: column; gap: var(--app-section-spacing); }

/* --- Media Queries, gesammelt am Dateiende ------------------------------- *
 * Sie stehen hier und nicht bei ihrem Bauteil, weil eine spaetere Grundregel mit
 * gleicher Spezifitaet die Ausnahme sonst schlaegt. Genau das passierte: die
 * Telefon-Abstaende fuer .dsa-ds-main standen vor der Grundregel fuer .dsa-ds-main
 * und wurden von ihr aufgehoben. Gefunden von codex review. */

@media (max-width: 767px) { .dsa-ds-button { height: var(--app-control-height-touch); } }

@media (max-width: 1023px) {
  .dsa-ds-nav { width: auto; min-width: var(--app-nav-sidebar-width); }
  .dsa-ds-nav-item { padding: var(--spacing-2); }
}

@media (max-width: 767px) {
  .dsa-ds-shell { flex-direction: column; }
  .dsa-ds-nav {
    order: 2; width: auto; min-width: 0; min-height: 0;
    position: sticky; bottom: 0; z-index: var(--app-layer-sticky);
    flex-direction: row; gap: 0;
    /* space-around verteilt freien Platz -- den gibt es hier nicht mehr, seit die
     * Eintraege mit flex: 1 1 0 selbst wachsen. Von codex review als wirkungslos
     * benannt; entfernt, damit der naechste Leser nicht nach einer Wirkung sucht. */
    border-right: none;
    border-top: var(--spacing-px) solid var(--ds-border-subtle);
    padding: var(--spacing-1) var(--spacing-2);
  }
  .dsa-ds-nav-group { display: none; }
  .dsa-ds-nav-item {
    flex-direction: column; gap: var(--spacing-0-5);
    min-height: var(--app-control-height-touch);
    justify-content: center; text-align: center;
    /* Die HANDY-Stufe der Marke, nicht die Desktop-Stufe. Das System nahm hier bisher
     * --app-text-label (12px, Desktop), obwohl die Marke body-xs auch fuer Handy
     * definiert (10px). Ein uebersehener Markenwert, kein Geschmack -- und in dieser
     * Leiste kostet er 12 px Beschriftungsbreite je Eintrag. */
    font-size: var(--app-text-label-mobile);
    /* Seitlich nur noch --spacing-0-5. Bei 63 px Eintragsbreite waren 8 px je Seite ein
     * Viertel des Eintrags; senkrecht bleibt --spacing-1, die Hoehe traegt
     * --app-control-height-touch. */
    padding: var(--spacing-1) var(--spacing-0-5);
    /* Die Eintraege TEILEN SICH die Breite, statt ihre Beschriftung zu behalten.
     *
     * Vorher behielt jeder seine Textbreite, und die Leiste lief ueber: auf 360 px sind fuenf
     * Eintraege 72 px, und ein Wort wie "Performance" braucht mehr. Ergebnis war eine
     * Scrollleiste, und der letzte Eintrag war nicht sichtbar -- im Supplier-Testsystem am
     * 2026-10-07 gesehen, nachdem dasselbe schon die Crew-App getroffen hatte.
     *
     * Thomas dazu: "scrollable ist, glaube ich, nicht so schoen." Richtig: eine untere Leiste
     * hat eine feste Zahl von Zielen, und die Breite teilt sich darauf auf. Wird ein Wort zu
     * lang, kuerzt es -- die Leiste bleibt vollstaendig sichtbar. Das Symbol darueber traegt
     * die Bedeutung ohnehin mit.
     *
     * Die Grenze davon, offen gesagt: ab etwa sieben Eintraegen kuerzt es so stark, dass die
     * Beschriftung nichts mehr aussagt. Das ist dann kein CSS-Problem, sondern eines der
     * Informationsarchitektur -- dann gehoert etwas zusammengefasst. */
    /* Nur der AKTIVE Eintrag traegt seinen Namen; die anderen sind Klickflaeche plus
     * Symbol. Entschieden von Thomas am 2026-10-07, nachdem beide Varianten gemessen
     * waren. Die Alternative -- alle Eintraege gleich breit und kuerzen -- loeste den
     * Ueberlauf, liess aber unter 390 px im Englischen Woerter abschneiden.
     *
     * Warum nicht nur Symbole: von den fuenf sind Dokument (Fulfillments) und Karte
     * (Payments) das Paar, das man am ehesten verwechselt, und das ist das Paar, bei
     * dem es um Geld geht. Diese Fassung behaelt den einen Namen, der die Frage
     * beantwortet, die eine untere Leiste am haeufigsten hat: wo bin ich gerade.
     *
     * Die nicht aktiven Eintraege schrumpfen NICHT unter die Mindest-Klickflaeche.
     * Ohne das min-width werden sie 16 px breit -- nur Symbol plus Padding -- und
     * damit praktisch untreffbar. Am Geraet gemessen, nicht geschaetzt. */
    flex: 0 0 auto; min-width: var(--app-control-height-touch);
  }
  .dsa-ds-nav-item-active { flex: 1 1 auto; min-width: 0; }
  /* Der Name der nicht aktiven Eintraege wird VERBORGEN, nicht entfernt: alle fuenf
   * bleiben fuer Vorleseprogramme vollstaendig lesbar. Deshalb dieselben Eigenschaften
   * wie .dsa-ds-sr-only, hier auf die Beschriftung angewandt. */
  .dsa-ds-nav-item:not(.dsa-ds-nav-item-active) > span {
    position: absolute;
    width: var(--spacing-px); height: var(--spacing-px);
    padding: 0; margin: calc(-1 * var(--spacing-px));
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  /* Der aktive Eintrag bekommt bei 320 px rund 97 px und bei 360 px rund 137 px -- das
   * reicht fuer jede der fuenf Beschriftungen in allen drei Sprachen. Die Ellipse bleibt
   * trotzdem als Netz: eine spaeter hinzukommende, laengere Beschriftung soll kuerzen
   * statt die Leiste zu sprengen. */
  .dsa-ds-nav-item-active > span {
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .dsa-ds-main { order: 1; padding: var(--spacing-4); gap: var(--spacing-4); }
}

/* Ab Tabletbreite teilt sich die Flaeche ein Drittel zu zwei Dritteln -- die schmale Karte
 * traegt die Zahlen, die breite das Diagramm. Hier unten und nicht beim Bauteil, weil eine
 * Grundregel NACH ihrer Ausnahme diese bei gleicher Spezifitaet wieder aufhebt. */
@media (min-width: 1024px) { .dsa-ds-split { grid-template-columns: 1fr 2fr; } }

@media (prefers-reduced-motion: reduce) {
  :where(.dsa-ds-root) * { transition-duration: var(--app-motion-reduced) !important; animation-duration: var(--app-motion-reduced) !important; }
}
