/* ============================================================================
 * LeadSuche — Master Stylesheet · Marketing Site + App Shell
 * Plus Jakarta Sans + Inter · blue → violet → orange-red brand gradient ·
 * navy-to-violet dark stages · single --ds-* token namespace
 *
 * Structure: @font-face → ONE :root (all tokens) → base/reset → typography →
 * shared primitives (forms bridge, alerts, blog, badges) → marketing
 * components (one-pager, landing sections, hero) → Phase-2 ds components
 * (buttons, cards, forms, tables, shells, sidebar, footer) → app components
 * (search/filter, records, billing, detail) → late component singletons →
 * @media print (always last).
 *
 * Cascade rule of thumb: a component's LAST definition block is its home;
 * earlier blocks only carry layout-era groundwork. Never re-style a
 * component by appending an override layer at the end of the file — edit
 * its home block instead.
 * ============================================================================ */

/* === Plus Jakarta Sans (variable font, wght 200–800, self-hosted) ===
   Display type, headings, buttons and navigation. */
@font-face {
  font-family: "Plus Jakarta Sans";
  src:
    url("../fonts/PlusJakartaSans-VariableFont.52d04440a9fa.woff2")
      format("woff2-variations"),
    url("../fonts/PlusJakartaSans-VariableFont.52d04440a9fa.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

/* === Display hyphen and punctuation ===
   Plus Jakarta Sans draws its hyphen almost as wide as an en dash, which
   turns "DACH-Raum" into "DACH–Raum" at display sizes, and its period and
   comma carry a wide left bearing that the tight display tracking turns into
   a visible gap ("Raum ."). This face borrows only those glyphs from Inter
   and leads the display stack. */
@font-face {
  font-family: "LS Display Hyphen";
  src:
    url("../fonts/inter/inter-latin-opsz-normal.4b73e2ff3024.woff2")
      format("woff2-variations"),
    url("../fonts/inter/inter-latin-opsz-normal.4b73e2ff3024.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+002C-002E, U+2010-2011;
}

/* === Inter (variable font, opsz + wght axes, Latin subset, self-hosted) ===
   Body copy, forms and app data; the optical-size axis gives the text cut
   and tabular figures for prices and tables. */
@font-face {
  font-family: "Inter";
  src:
    url("../fonts/inter/inter-latin-opsz-normal.4b73e2ff3024.woff2")
      format("woff2-variations"),
    url("../fonts/inter/inter-latin-opsz-normal.4b73e2ff3024.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}

/* === Bootstrap Icons (self-hosted) === */
@font-face {
  font-family: "bootstrap-icons";
  src:
    url("../fonts/bootstrap-icons/bootstrap-icons.cc1e5eda776b.woff2") format("woff2"),
    url("../fonts/bootstrap-icons/bootstrap-icons.ba49e8448923.woff") format("woff");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* === Brand Variables === */
:root {
  /* Bootstrap variable overrides — re-point Bootstrap's own tokens to the
       brand --ds-* set so framework components inherit the design system. */
  --bs-primary: var(--ds-blue);
  --bs-primary-rgb: var(--ds-blue-rgb);
  --bs-link-color: var(--ds-blue);
  --bs-link-hover-color: var(--ds-blue-dark);
  --bs-body-font-family: var(--ds-font-body);
  --bs-body-color: var(--ds-ink);
  --bs-heading-color: var(--ds-ink);
  --bs-border-radius: var(--ds-radius-xs);
  --bs-border-radius-lg: var(--ds-radius-sm);
  --bs-border-radius-xl: var(--ds-radius-md);

  color-scheme: light;

  /* ========================================================================
     * .ds-* Design System Tokens — the sole token set, defined in this ONE
     * :root block. Direction: the logo blue and orange-red joined by a
     * violet into one brand gradient, navy-to-violet dark stages for the
     * hero, the comparison, the closing band and the footer, white and
     * softly tinted light sections in between. Every text pair below was
     * measured against WCAG 2.x (relative luminance, 4.5:1 floor).
     * ====================================================================== */

  /* Brand. The interactive blue sits between the logo blue (#1374ea) and
     the former #074eb9: white on it 6.67:1, it on white 6.67:1, on the tint
     6.22:1. Links, focus rings and the start of every gradient. */
  --ds-blue: #0b55c8;
  --ds-blue-rgb: 11, 85, 200;
  --ds-blue-dark: #0846a6;
  --ds-blue-deep: #06388a;
  /* Violet joins blue and orange-red. White on it 6.37:1, it on white
     6.37:1, so it can end a button gradient and colour text. */
  --ds-purple: #6a3fd6;
  --ds-purple-rgb: 106, 63, 214;
  --ds-purple-dark: #5730b8;
  --ds-purple-light: #c4b5fd;
  /* Logo orange-red: 3.71:1 on white, so decoration only (gradient ends,
     glows, hairlines), never text and never under white text. Readable
     red text uses --ds-red-deep (5.37:1 on white, 5.01:1 on the tint). */
  --ds-red: #ee4a33;
  --ds-red-rgb: 238, 74, 51;
  --ds-red-deep: #c4362c;
  /* Readable accent pair for highlight badges (5.21:1). */
  --ds-accent-text: #b83a12;
  --ds-accent-tint-bg: #fff1eb;

  /* Ink (text), a navy with an indigo cast. --ds-ink 17.14:1 on white and
     15.99:1 on the tint; --ds-ink-soft 7.73:1 and --ds-ink-mute 5.52:1 on
     the tint; --ds-ink-dim is 2.90:1, icons and disabled states only. */
  --ds-ink: #141a33;
  --ds-ink-rgb: 20, 26, 51;
  --ds-ink-soft: #464d6b;
  --ds-ink-mute: #5c6380;
  --ds-ink-dim: #9097b0;

  /* Surfaces. The light tints carry a trace of the brand blue. */
  --ds-surface: #ffffff;
  --ds-surface-tint: #f5f7fc;
  --ds-surface-muted: #edf0fa;
  --ds-surface-3: #e3e7f3;
  --ds-surface-4: #cdd3e4;
  /* Dark stage: near-black navy, deep navy and a violet-black end stop. */
  --ds-surface-dark: #0a0f1a;
  --ds-surface-dark-2: #0f1a30;
  --ds-surface-dark-violet: #1a1035;
  --ds-surface-dark-border: rgba(var(--ds-white-rgb), 0.1);
  /* Glass panels on the dark stage (cards, the comparison table). */
  --ds-surface-glass: rgba(var(--ds-white-rgb), 0.05);
  --ds-surface-glass-strong: rgba(var(--ds-white-rgb), 0.08);
  --ds-border-glass: rgba(var(--ds-white-rgb), 0.12);

  /* Borders: solid, blue-tinted values, so a border looks the same on every
     light surface. --ds-border-control clears the 3:1 non-text floor (WCAG
     1.4.11) a form control's outline needs (3.33:1 on white, 3.10:1 on the
     tint); the softer borders are dividers. */
  --ds-border-subtle: #e8ebf5;
  --ds-border: #dde2ef;
  --ds-border-strong: #c5cce0;
  --ds-border-gray: #e3e7f3;
  --ds-border-control: #858ca8;

  /* Status (WCAG AA on --ds-surface and --ds-surface-tint) */
  --ds-success: #0a7a3e;
  --ds-success-rgb: 10, 122, 62;
  --ds-warn: #b45309;
  --ds-warn-rgb: 180, 83, 9;
  --ds-danger: #c42b1f;
  --ds-danger-rgb: 196, 43, 31;
  --ds-info: #0b55c8;
  --ds-info-rgb: 11, 85, 200;

  /* Status-Tints (low-opacity backgrounds for alerts, badges, icon-halos).
       Use as: background: var(--ds-success-tint); color: var(--ds-success); */
  --ds-success-tint: rgba(var(--ds-success-rgb), 0.1);
  --ds-warn-tint: rgba(var(--ds-warn-rgb), 0.1);
  --ds-danger-tint: rgba(var(--ds-danger-rgb), 0.1);
  --ds-info-tint: rgba(var(--ds-info-rgb), 0.1);
  /* Brighter "live/positive" green for activity dots only; never text. The
       status green --ds-success is reserved for text, alerts and forms. */
  --ds-success-bright: #22c55e;
  --ds-success-bright-rgb: 34, 197, 94;
  /* Deep status inks for alert text on tinted alert backgrounds. */
  --ds-success-deep: #0b5c30;
  --ds-warn-deep: #6b3e0a;
  /* Danger hover fill: darker than --ds-danger (white text 7.11:1). */
  --ds-danger-bright: #a9241a;
  --ds-danger-bright-rgb: 169, 36, 26;
  /* Amber accent (sticky-CTA star) + bright amber channel used for warning
       tints on dark surfaces. */
  --ds-amber: #f5a524;
  --ds-warn-bright-rgb: 245, 158, 11;

  /* Solid pastel tint pairs for ds-badge (bg + text per status), each pair
       at least 6.5:1. The violet pair marks brand highlights (7.47:1). */
  --ds-neutral-tint-bg: #ebf0fc;
  --ds-neutral-tint-text: #0a44a8;
  --ds-violet-tint-bg: #f1ecfe;
  --ds-violet-tint-text: #5328c0;
  --ds-success-tint-bg: #e6f4ec;
  --ds-success-tint-text: #0b5c30;
  --ds-warn-tint-bg: #fdf1e2;
  --ds-warn-tint-text: #8a4209;
  --ds-danger-tint-bg: #fce9e7;
  --ds-danger-tint-text: #9e1f16;
  --ds-info-tint-bg: #ebf0fc;
  --ds-info-tint-text: #0a44a8;

  /* On the dark stage. Measured against the brightest point the mesh glows
     reach (#143568): strong 10.93:1, body 8.14:1, muted 5.57:1, accent
     6.83:1; on the base navy all clear 8:1. --ds-ink-on-dark-subtle
     (5.06:1 on navy, 3.53:1 on a glow peak) is for separators and icons. */
  --ds-ink-on-dark-strong: #f1f3ff;
  --ds-ink-on-dark-body: #cdd3ee;
  --ds-ink-on-dark-muted: #a6afd3;
  --ds-ink-on-dark-subtle: #7f89b3;
  --ds-ink-on-dark-accent: #9ec5ff;
  /* Lighter blues for dark surfaces, where --ds-blue would read as
     near-black (2.87:1): -glow for ambient glows, -on-dark for readable
     accents. Declared as channels so every use runs through rgba(). */
  --ds-blue-glow-rgb: 31, 116, 236;
  --ds-blue-on-dark-rgb: 158, 197, 255;
  --ds-lavender: #c4b5fd;
  --ds-lavender-light: #ddd6fe;
  --ds-success-on-dark: #4ade80;
  --ds-danger-on-dark: #fca5a5;
  --ds-warn-on-dark: #fcd34d;
  --ds-info-on-dark: #9ec5ff;

  /* Gradients. The full brand sweep ends in the logo orange-red and is
     therefore decoration only (accent lines, borders, glows). Anything that
     carries white text or is text itself uses a stop set in which every
     stop clears 4.5:1: --ds-gradient-cta (white on blue 6.67:1, on violet
     6.37:1), --ds-gradient-text (on white 6.67 / 6.37 / 5.37:1, on the tint
     6.22 / 5.95 / 5.01:1) and --ds-gradient-text-on-dark (above 9.4:1 on
     the navy, 6.34:1 on a glow peak). */
  --ds-gradient-brand: linear-gradient(
    135deg,
    var(--ds-blue) 0%,
    var(--ds-purple) 55%,
    var(--ds-red) 100%
  );
  --ds-gradient-cta: linear-gradient(
    120deg,
    var(--ds-blue) 0%,
    var(--ds-purple) 100%
  );
  --ds-gradient-cta-active: linear-gradient(
    120deg,
    var(--ds-blue-dark) 0%,
    var(--ds-purple-dark) 100%
  );
  --ds-gradient-text: linear-gradient(
    100deg,
    var(--ds-blue) 0%,
    var(--ds-purple) 55%,
    var(--ds-red-deep) 100%
  );
  --ds-gradient-text-on-dark: linear-gradient(
    100deg,
    var(--ds-ink-on-dark-accent) 0%,
    var(--ds-lavender) 50%,
    var(--ds-danger-on-dark) 100%
  );
  /* Score meters (Kaufbereitschaft, Innovationsgrad, digitale Reife) fill
     blue to violet and never reach the orange-red: a high score drawn in
     red would read as a warning. The -on-dark pair is the pastel version. */
  --ds-gradient-meter: linear-gradient(
    90deg,
    var(--ds-blue) 0%,
    var(--ds-purple) 100%
  );
  --ds-gradient-meter-on-dark: linear-gradient(
    90deg,
    var(--ds-ink-on-dark-accent) 0%,
    var(--ds-lavender) 100%
  );
  /* Older name of the on-dark text gradient, kept for existing references. */
  --ds-gradient-brand-on-dark: var(--ds-gradient-text-on-dark);
  --ds-gradient-brand-soft: linear-gradient(
    135deg,
    rgba(var(--ds-blue-rgb), 0.08) 0%,
    rgba(var(--ds-purple-rgb), 0.07) 55%,
    rgba(var(--ds-red-rgb), 0.06) 100%
  );
  /* Icon tiles: white glyph on blue to violet (6.67 / 6.37:1). */
  --ds-gradient-icon-tile: linear-gradient(
    135deg,
    var(--ds-blue) 0%,
    var(--ds-purple) 100%
  );
  /* The dark stage: a navy-to-violet base under three soft glows (blue top
     left, violet right, orange-red low right). The glows stay below the
     alpha the on-dark ink pairs above were measured against. */
  --ds-gradient-mesh-dark:
    radial-gradient(
      60% 70% at 12% 8%,
      rgba(var(--ds-blue-glow-rgb), 0.3) 0%,
      transparent 62%
    ),
    radial-gradient(
      50% 60% at 88% 30%,
      rgba(var(--ds-purple-rgb), 0.32) 0%,
      transparent 65%
    ),
    radial-gradient(
      45% 55% at 78% 100%,
      rgba(var(--ds-red-rgb), 0.16) 0%,
      transparent 70%
    ),
    linear-gradient(
      160deg,
      var(--ds-surface-dark-2) 0%,
      var(--ds-surface-dark) 55%,
      var(--ds-surface-dark-violet) 100%
    );
  /* The softly tinted light sections: the tint under two faint glows. */
  --ds-gradient-mesh-light:
    radial-gradient(
      50% 60% at 0% 0%,
      rgba(var(--ds-blue-rgb), 0.06) 0%,
      transparent 70%
    ),
    radial-gradient(
      45% 55% at 100% 100%,
      rgba(var(--ds-purple-rgb), 0.06) 0%,
      transparent 70%
    ),
    var(--ds-surface-tint);
  /* The fine grid laid over the dark stage, 48px cells, white at 4.5%. */
  --ds-pattern-grid-dark:
    linear-gradient(rgba(var(--ds-white-rgb), 0.045) 1px, transparent 1px),
    linear-gradient(
      90deg,
      rgba(var(--ds-white-rgb), 0.045) 1px,
      transparent 1px
    );

  /* Neutrals (universal, non-brand) + their RGB channels for rgba() */
  --ds-white: #ffffff;
  --ds-black: #000000;
  --ds-white-rgb: 255, 255, 255;
  --ds-black-rgb: 0, 0, 0;

  /* Typography. Plus Jakarta Sans sets display type, headings, buttons and
     navigation; Inter sets body copy, forms and app data (tabular figures).
     Jakarta falls back to Inter for the glyphs it lacks (≈, →), and its
     hyphen comes from Inter through the "LS Display Hyphen" face. */
  --ds-font-display:
    "LS Display Hyphen", "Plus Jakarta Sans", "Inter", system-ui, -apple-system,
    "Segoe UI", Roboto, sans-serif;
  --ds-font-body:
    "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --ds-font-mono: ui-monospace, "SF Mono", Consolas, monospace;

  /* Type scale: display (hero) · h1 (page hero) · h2 (section) · h3 ·
     h4 (card titles) · lead · body · small · caption · eyebrow. */
  --ds-text-hero-dramatic: clamp(2.5rem, 1.55rem + 3.6vw, 4rem);
  --ds-text-hero-standard: clamp(2.125rem, 1.6rem + 2.2vw, 3.25rem);
  --ds-text-section: clamp(1.875rem, 1.4rem + 1.9vw, 2.75rem);
  --ds-text-title-3: clamp(1.375rem, 1.2rem + 0.7vw, 1.75rem);
  /* App page H1: a fixed 24px, app pages are tools, not landing pages. */
  --ds-text-page-title: 1.5rem;
  --ds-text-subsection: clamp(1.125rem, 1.05rem + 0.3vw, 1.25rem);
  --ds-text-lead: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  --ds-text-lg: 1.125rem;
  --ds-text-body: 1rem;
  --ds-text-body-sm: 0.875rem;
  --ds-text-caption: 0.8125rem;
  --ds-text-eyebrow: 0.75rem;

  --ds-lh-display: 1.04;
  --ds-lh-tight: 1.12;
  --ds-lh-snug: 1.3;
  --ds-lh-base: 1.55;
  --ds-lh-prose: 1.7;

  --ds-fw-regular: 400;
  --ds-fw-medium: 500;
  --ds-fw-semi: 600;
  --ds-fw-bold: 700;
  --ds-fw-black: 800;

  --ds-tracking-display: -0.035em;
  --ds-tracking-tight: -0.025em;
  --ds-tracking-snug: -0.012em;
  --ds-tracking-base: 0;
  --ds-tracking-eyebrow: 0.12em;
  --ds-tracking-meta: 0.06em;

  /* Chrome and layout rhythm. --ds-gutter is the only page gutter: a
     section adds no inline padding of its own. --ds-control-h keeps inputs
     and default buttons at the 44px touch target. */
  --ds-site-header-h: 72px;
  --ds-app-header-h: 64px;
  --ds-sidebar-width: 248px;
  --ds-sidebar-width-collapsed: 64px;
  --ds-gutter: clamp(1rem, 4vw, 2.5rem);
  --ds-section-y: clamp(4rem, 8vw, 7rem);
  --ds-control-h: 44px;
  --ds-control-h-sm: 36px;
  --ds-control-h-lg: 52px;

  /* Radius */
  --ds-radius-xs: 6px;
  --ds-radius-sm: 10px;
  --ds-radius-md: 14px;
  --ds-radius-lg: 20px;
  --ds-radius-xl: 28px;
  --ds-radius-full: 9999px;

  /* Shadows: soft, long and tinted with the brand blue on light surfaces;
     deep and dark under the floating product mockup. */
  --ds-shadow-sm:
    0 1px 2px rgba(var(--ds-ink-rgb), 0.05),
    0 1px 3px rgba(var(--ds-blue-rgb), 0.04);
  --ds-shadow-md:
    0 1px 2px rgba(var(--ds-ink-rgb), 0.04),
    0 10px 28px -8px rgba(var(--ds-blue-rgb), 0.16);
  --ds-shadow-lg:
    0 2px 6px rgba(var(--ds-ink-rgb), 0.05),
    0 28px 56px -16px rgba(var(--ds-blue-rgb), 0.26);
  --ds-shadow-xl:
    0 2px 6px rgba(var(--ds-black-rgb), 0.12),
    0 48px 96px -24px rgba(var(--ds-black-rgb), 0.55);
  --ds-shadow-gradient: 0 16px 40px -12px rgba(var(--ds-purple-rgb), 0.35);
  --ds-shadow-icon-tile: 0 8px 18px -6px rgba(var(--ds-blue-rgb), 0.5);
  --ds-shadow-icon-tile-lg: 0 12px 24px -8px rgba(var(--ds-purple-rgb), 0.5);
  --ds-shadow-float: 0 18px 40px -14px rgba(var(--ds-ink-rgb), 0.28);
  /* Glow under the gradient CTA: violet below, blue closer in. */
  --ds-shadow-primary:
    inset 0 1px 0 rgba(var(--ds-white-rgb), 0.18),
    0 6px 16px -6px rgba(var(--ds-blue-rgb), 0.6);
  --ds-shadow-primary-hover:
    inset 0 1px 0 rgba(var(--ds-white-rgb), 0.2),
    0 10px 28px -6px rgba(var(--ds-purple-rgb), 0.6),
    0 4px 12px -4px rgba(var(--ds-blue-rgb), 0.5);
  /* Recommended plan: a brand-coloured halo around the gradient frame. */
  --ds-shadow-glow-featured:
    0 0 0 6px rgba(var(--ds-purple-rgb), 0.06),
    0 30px 60px -20px rgba(var(--ds-purple-rgb), 0.38);
  --ds-shadow-danger-hover: 0 4px 12px rgba(var(--ds-danger-rgb), 0.25);
  --ds-shadow-toast: 0 16px 36px rgba(var(--ds-ink-rgb), 0.24);
  --ds-shadow-modal: 0 24px 56px rgba(var(--ds-ink-rgb), 0.22);
  --ds-shadow-tour: 0 22px 48px rgba(var(--ds-ink-rgb), 0.26);
  --ds-shadow-tooltip: 0 8px 18px rgba(var(--ds-ink-rgb), 0.2);
  --ds-shadow-pill:
    inset 0 1px 0 rgba(var(--ds-white-rgb), 0.7),
    0 2px 8px rgba(var(--ds-blue-rgb), 0.06);
  --ds-shadow-pill-on-dark:
    inset 0 1px 0 rgba(var(--ds-white-rgb), 0.06),
    0 8px 24px rgba(var(--ds-black-rgb), 0.25);
  /* Dimming backdrop behind modals, tour steps and drawers. */
  --ds-backdrop: rgba(var(--ds-ink-rgb), 0.55);

  /* Focus rings. The inner 3px band is an OPAQUE brand tone so the ring
     itself clears the WCAG 1.4.11 3:1 floor against the page (--ds-blue
     6.67:1 on white); the outer 16% halo is decorative only. Never reduce
     the opaque band to a translucent tint — that was the 1.30:1
     regression. The -on-brand/-on-danger variants add an inset white band
     so the ring stays visible on a control whose own fill is blue or red.
     On the dark stage --ds-blue measures 2.87:1, so dark contexts use the
     -on-dark ring in the on-dark accent (9.82:1 on the navy). */
  --ds-shadow-focus:
    0 0 0 3px var(--ds-blue), 0 0 0 6px rgba(var(--ds-blue-rgb), 0.16);
  --ds-shadow-focus-on-brand:
    inset 0 0 0 2px var(--ds-white), 0 0 0 3px var(--ds-blue),
    0 0 0 6px rgba(var(--ds-blue-rgb), 0.16);
  --ds-shadow-focus-on-dark:
    inset 0 0 0 2px var(--ds-white), 0 0 0 3px var(--ds-ink-on-dark-accent),
    0 0 0 6px rgba(var(--ds-blue-on-dark-rgb), 0.2);
  --ds-shadow-focus-on-danger:
    inset 0 0 0 2px var(--ds-white), 0 0 0 3px var(--ds-danger),
    0 0 0 6px rgba(var(--ds-danger-rgb), 0.16);
  --ds-shadow-focus-danger:
    0 0 0 3px var(--ds-danger), 0 0 0 6px rgba(var(--ds-danger-rgb), 0.16);
  --ds-shadow-focus-success:
    0 0 0 3px var(--ds-success), 0 0 0 6px rgba(var(--ds-success-rgb), 0.16);
  /* Persistent field-validation halos. The states above are focus rings and
       vanish with the focus; .ls-field-invalid / .ls-field-valid and the
       server-rendered .is-invalid stay on the control after blur, so they use
       the soft tint instead. */
  --ds-shadow-field-danger: 0 0 0 4px rgba(var(--ds-danger-rgb), 0.16);
  --ds-shadow-field-success: 0 0 0 4px rgba(var(--ds-success-rgb), 0.16);

  /* Motion */
  --ds-dur-fast: 150ms;
  --ds-dur-base: 220ms;
  --ds-dur-slow: 600ms;
  --ds-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ds-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ds-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);

  /* Spacing (4px rhythm). The `-N-5` half steps below 20px are deliberate:
     component-internal padding (button, badge, chip, input) lives between
     the whole steps, and a scale that omits them forces every small
     component off-scale by construction. Layout rhythm still uses the
     whole steps only. */
  --ds-space-0-5: 2px;
  --ds-space-1: 4px;
  --ds-space-1-5: 6px;
  --ds-space-2: 8px;
  --ds-space-2-5: 10px;
  --ds-space-3: 12px;
  --ds-space-3-5: 14px;
  --ds-space-4: 16px;
  --ds-space-4-5: 18px;
  --ds-space-5: 20px;
  --ds-space-6: 24px;
  --ds-space-8: 32px;
  --ds-space-10: 40px;
  --ds-space-12: 48px;
  --ds-space-16: 64px;
  --ds-space-20: 80px;
  --ds-space-24: 96px;

  /* Z-index scale — the deliberate stacking order of every floating
       layer: consent banner < modal backdrop < modal/filter sheet < toast
       < onboarding tour. An open modal must never be painted over by the
       cookie banner. */
  --ds-z-base: 1;
  --ds-z-dropdown: 1000;
  --ds-z-sticky: 1020;
  --ds-z-fixed: 1030;
  --ds-z-consent: 1035;
  --ds-z-modal-backdrop: 1040;
  --ds-z-modal: 1050;
  --ds-z-toast: 1080;
  --ds-z-tour: 1090;

  /* Cards: only interactive cards lift; static cards keep still. */
  --ds-card-lift: -4px;
  --ds-card-transition:
    transform var(--ds-dur-base) var(--ds-ease-out),
    box-shadow var(--ds-dur-base) var(--ds-ease-standard),
    border-color var(--ds-dur-fast) var(--ds-ease-standard);
}

/* === Base / Reset === */
* {
  box-sizing: border-box;
}
html {
  scroll-behavior: smooth;
}
section[id] {
  scroll-margin-top: calc(var(--ds-site-header-h) + var(--ds-space-4));
}

/* Safety net against accidental horizontal scroll on mobile from oversized
   decorative layers (glow orbs, hero gradients, etc.). `clip` is preferred
   over `hidden` because it does not establish a scroll container and so
   does not break `position: sticky` ancestors. */
html,
body {
  overflow-x: clip;
}

body {
  font-family: var(--ds-font-body);
  font-optical-sizing: auto;
  color: var(--ds-ink);
  font-size: var(--ds-text-body);
  line-height: var(--ds-lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: var(--ds-surface);
}

/* Short paragraphs avoid a one-word last line where the browser can. */
p {
  text-wrap: pretty;
}

a {
  color: var(--ds-blue);
  text-decoration: none;
  transition: color var(--ds-dur-fast) var(--ds-ease-standard);
}
a:hover {
  color: var(--ds-blue-dark);
}

::selection {
  background: rgba(var(--ds-purple-rgb), 0.18);
  color: var(--ds-ink);
}

/* === Typography ===
   The type scale: display (--ds-text-hero-dramatic, the home hero only),
   h1 (--ds-text-hero-standard, page heroes), h2 (--ds-text-section), h3
   (--ds-text-title-3), h4 (--ds-text-subsection, card titles), lead, body,
   small, caption and eyebrow. Headings are Plus Jakarta Sans with tight
   tracking that loosens as the size drops. Components set their own classed
   sizes; these element rules are the fallback ramp for unclassed headings,
   one step below the marketing scale so an app card heading stays calm. */
h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6 {
  font-family: var(--ds-font-display);
  color: var(--ds-ink);
  font-weight: var(--ds-fw-bold);
  line-height: var(--ds-lh-snug);
  margin-bottom: 1rem;
  letter-spacing: var(--ds-tracking-snug);
  /* Balanced lines keep a heading from ending on a one-word widow. */
  text-wrap: balance;
}
h1,
.h1 {
  font-size: var(--ds-text-section);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-tight);
}
h2,
.h2 {
  font-size: var(--ds-text-title-3);
  line-height: 1.2;
  letter-spacing: var(--ds-tracking-tight);
}
h3,
.h3 {
  font-size: var(--ds-text-subsection);
}
h4,
.h4 {
  font-size: var(--ds-text-lg);
}
h5,
.h5 {
  font-size: var(--ds-text-body);
}
h6,
.h6 {
  font-size: var(--ds-text-body-sm);
}

/* Prices, counters and table figures keep their columns. */
.ds-num {
  font-variant-numeric: tabular-nums;
}

/* Keeps a compound such as "DACH-Raum" from breaking at its hyphen. */
.ds-nowrap {
  white-space: nowrap;
}

/* Spinning */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Shake */
@keyframes shake {
  0%,
  100% {
    transform: translateX(0);
  }
  25%,
  75% {
    transform: translateX(-5px);
  }
  50% {
    transform: translateX(5px);
  }
}

/* === Forms — the ONE sanctioned crispy bridge ===
   Crispy renders Bootstrap form classes (.form-control/.form-select); this
   bridge restyles them to the exact computed values of the canonical
   .ds-form-control so crispy output is visually indistinguishable from
   hand-written ds-form markup. Keep both definitions in sync. */
.form-control,
.form-select {
  min-height: var(--ds-control-h);
  border: 1px solid var(--ds-border-control);
  border-radius: var(--ds-radius-sm);
  padding: var(--ds-space-2-5) var(--ds-space-3);
  font-size: var(--ds-text-body);
  line-height: 1.4;
  /* background-color (not the shorthand) — .form-select carries Bootstrap's
       chevron as a background-image that must survive this restyle. */
  background-color: var(--ds-surface);
  color: var(--ds-ink);
  transition:
    border-color var(--ds-dur-fast) var(--ds-ease-out),
    box-shadow var(--ds-dur-fast) var(--ds-ease-out);
}
.form-control:focus,
.form-select:focus {
  border-color: var(--ds-blue);
  box-shadow: var(--ds-shadow-focus);
  outline: none;
}
/* Disabled parity with .ds-form-control:disabled — without it a crispy-
   rendered disabled field looked fully enabled next to a hand-written one. */
.form-control:disabled,
.form-select:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  background-color: var(--ds-surface-tint);
}
/* Error parity with .ds-form-control--error. The shake is the one deliberate
   difference: this class arrives with a server-rendered response, where the
   nudge points at the field that was rejected. The --error modifier is set
   live while typing, where a shake on every keystroke would be punishing. */
.form-control.is-invalid,
.form-select.is-invalid {
  border-color: var(--ds-danger);
  background-color: var(--ds-danger-tint);
  box-shadow: var(--ds-shadow-field-danger);
  animation: shake var(--ds-dur-fast) ease-in-out;
}
.form-control.is-invalid:focus-visible,
.form-select.is-invalid:focus-visible {
  box-shadow: var(--ds-shadow-focus-danger);
}
/* Select chevron — the ds-form-control/.form-select background fills would
   otherwise erase Bootstrap's arrow. The element-qualified selectors outrank
   both base classes regardless of source order. */
select.form-select,
select.form-control,
select.ds-form-control {
  padding-right: 36px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%230e1a2e' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 14px 14px;
  appearance: none;
  -webkit-appearance: none;
}
.form-control::placeholder {
  color: var(--ds-ink-mute);
}
.form-label {
  font-weight: var(--ds-fw-semi);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink);
  margin-bottom: var(--ds-space-1-5);
}
/* Required fields carry no asterisk: every field is required unless its
   label says "(optional)". */
.asteriskField {
  display: none;
}
.form-text {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}
.invalid-feedback {
  font-size: var(--ds-text-caption);
  color: var(--ds-danger);
}
.form-check-input {
  border: 1px solid var(--ds-border-control);
}
.form-check-input:checked {
  background-color: var(--ds-blue);
  border-color: var(--ds-blue);
}
.form-check-input:focus {
  box-shadow: var(--ds-shadow-focus);
  border-color: var(--ds-blue);
}

/* === Blog ===
   Article cards on the blog index and under an article: a link card with a
   cluster badge, a Jakarta title, two lines of excerpt and a meta row. The
   card lifts like every a.ds-card and shows the brand hairline on top. */
.blog-card {
  color: var(--ds-ink);
  height: 100%;
  padding: var(--ds-space-6);
  border-radius: var(--ds-radius-lg);
}
.blog-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--ds-gradient-brand);
  opacity: 0;
  transition: opacity var(--ds-dur-base) var(--ds-ease-standard);
}
.blog-card:hover::before,
.blog-card:focus-visible::before {
  opacity: 1;
}
/* Cluster pill on blog cards — pure layout modifier on ds-badge--neutral.
   align-self stops the flex-column card from stretching the inline badge to
   full card width; the badge skin itself stays canonical. */
.blog-card .badge-cluster {
  align-self: flex-start;
  margin-bottom: var(--ds-space-1);
}
.blog-card h2,
.blog-card h3 {
  color: var(--ds-ink);
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  line-height: var(--ds-lh-snug);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blog-card .excerpt {
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blog-card .meta {
  display: flex;
  gap: var(--ds-space-4);
  align-items: center;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  /* Auto margin, not a growing excerpt, keeps the meta rows of a card row
     level: a stretched excerpt box would show a fourth line past the clamp. */
  margin-top: auto;
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-border-subtle);
}

.ds-prose.blog-article {
  font-size: 1.0625rem;
}

/* "Das Wichtigste auf einen Blick" box at the top of an article. */
.key-takeaways,
.ds-prose .alert-info {
  margin: 0 0 var(--ds-space-10);
  padding: var(--ds-space-6) var(--ds-space-6) var(--ds-space-5);
  background: var(--ds-gradient-brand-soft);
  border: 1px solid rgba(var(--ds-purple-rgb), 0.16);
  border-left: 4px solid var(--ds-purple);
  border-radius: var(--ds-radius-md);
  color: var(--ds-ink-soft);
}
.ds-prose .key-takeaways h2 {
  margin: 0 0 var(--ds-space-3);
  padding: 0;
  border: 0;
  font-size: var(--ds-text-lg);
  color: var(--ds-purple-dark);
}
.ds-prose .key-takeaways ul {
  margin-bottom: 0;
}
.ds-prose .alert-info > :last-child {
  margin-bottom: 0;
}

/* === Tables === */
.table {
  --bs-table-color: var(--ds-ink);
  border-collapse: separate;
  border-spacing: 0;
}
.table thead th {
  background: var(--ds-surface-tint);
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-caption);
  text-transform: none;
  letter-spacing: 0;
  font-weight: var(--ds-fw-semi);
  border-bottom: 1px solid var(--ds-border);
  padding: var(--ds-space-3) var(--ds-space-4);
}
.table tbody td {
  padding: var(--ds-space-3) var(--ds-space-4);
  border-color: var(--ds-surface-3);
  vertical-align: middle;
}

/* ds-table is the canonical DS table: sentence-case header on the tint,
   14px rows with tabular figures, a tint hover. Long German headers
   ("Freigeschaltet") read better in sentence case than in capitals. */
.ds-table {
  --bs-table-color: var(--ds-ink);
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
}
.ds-table thead th {
  background: var(--ds-surface-tint);
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-caption);
  text-transform: none;
  letter-spacing: 0;
  font-weight: var(--ds-fw-semi);
  border-bottom: 1px solid var(--ds-border);
  padding: var(--ds-space-3) var(--ds-space-4);
  white-space: nowrap;
}
.ds-table tbody td {
  padding: var(--ds-space-3) var(--ds-space-4);
  font-size: var(--ds-text-body-sm);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--ds-border-subtle);
  vertical-align: middle;
}
.ds-table tbody tr {
  transition: background var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-table tbody tr:hover {
  background: var(--ds-surface-tint);
}
.ds-table tbody tr:last-child td {
  border-bottom: 0;
}

/* Removes the default table margin when the table sits directly inside a
   card body that already handles spacing. */
.ds-table--flush {
  margin: 0;
}

/* Right-align a column header in data tables (e.g. numeric columns). */
.ds-col--right {
  text-align: right;
}

/* Numeric data column — right-aligned with fixed-width digits to keep
   values visually aligned across rows. */
.ds-col--numeric {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Canonical DS wrapper for data tables — provides the border-radius, scroll
   container, and box-shadow that were previously on .data-table-wrap. */
.ds-table-wrap {
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  overflow: auto;
}

/* Compact column for row action buttons (download, retry). Sits mid-table
   right after the status column so the primary action stays visible first
   when the table scrolls horizontally on small screens. */
.ds-table__actions-cell {
  width: 80px;
  white-space: nowrap;
}

/* Fixed-width, centred column for row-selection checkboxes. */
.ds-lead-table__row-select {
  width: 40px;
  padding-inline: 8px;
  text-align: center;
}

/* Token-mapped spacing utilities for elements that need a single margin-top
   override without a bespoke block selector. */
.ds-mt-3 {
  margin-top: var(--ds-space-3);
}
.ds-mt-8 {
  margin-top: var(--ds-space-8);
}

/* === Alerts === */
.alert {
  border: 1px solid transparent;
  border-radius: var(--ds-radius-sm);
  padding: 0.85rem 1rem;
  font-size: var(--ds-text-body-sm);
}
.alert-info {
  background: rgba(var(--ds-blue-rgb), 0.08);
  color: var(--ds-blue-dark);
  border-color: rgba(var(--ds-blue-rgb), 0.25);
}

/* === Skip-Link === */
.skip-link {
  position: absolute;
  top: -100px;
  left: 0;
  background: var(--ds-ink);
  color: var(--ds-white);
  padding: var(--ds-space-3) var(--ds-space-5);
  /* Above the whole z-ramp on purpose: the skip link is the keyboard user's
     escape hatch and must stay reachable even with a modal or toast open. */
  z-index: 9999;
  border-radius: 0 0 var(--ds-radius-sm) 0;
}
.skip-link:focus {
  top: 0;
  color: var(--ds-white);
}

/* === Lazy-Loaded Images === */
img[loading="lazy"] {
  transition: opacity 0.4s ease-in;
}
img {
  max-width: 100%;
  height: auto;
}

/* Breadcrumb navigation strip — small muted text row above hero headlines.
   Replaces the Bootstrap .small + .text-muted + .mb-3 combination. */
.ds-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-1-5);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
  margin-bottom: var(--ds-space-4);
}
.ds-breadcrumb a {
  color: var(--ds-blue);
  text-decoration: none;
}
.ds-breadcrumb a:hover {
  color: var(--ds-ink);
  text-decoration: underline;
}

/* === Blog Cluster Chips (blog_index.html filter row) ===
   White pills with a hairline; the active cluster wears the CTA gradient
   (white text 6.37:1 at its violet end). */
.cluster-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  margin: 0 0 var(--ds-space-10);
}
.cluster-chip {
  display: inline-flex;
  align-items: center;
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-4);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  color: var(--ds-ink-soft);
  border-radius: var(--ds-radius-full);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  text-decoration: none;
  box-shadow: var(--ds-shadow-sm);
  transition:
    border-color var(--ds-dur-fast) var(--ds-ease-standard),
    color var(--ds-dur-fast) var(--ds-ease-standard),
    box-shadow var(--ds-dur-fast) var(--ds-ease-standard);
}
.cluster-chip:hover {
  border-color: rgba(var(--ds-blue-rgb), 0.45);
  color: var(--ds-ink);
}
.cluster-chip.is-active,
.cluster-chip.is-active:hover {
  background: var(--ds-gradient-cta);
  border-color: transparent;
  color: var(--ds-white);
  box-shadow: var(--ds-shadow-primary);
}
.cluster-chip:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* ============================================================
   ONE-PAGER HOME
   Classes use the .ds-op- prefix within the .ds- design system.
   ============================================================ */

.container {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--ds-gutter);
}

/* Inside a .container the gutter already exists; the inner column must not
   add a second one. */
.container .ds-section__inner {
  padding-inline: 0;
}

/* Width modifiers carry doubled specificity so they always beat the
   .ds-section__inner base (max-width 1200px, defined at the Phase-2
   component home) regardless of source order. */
.ds-section__inner.ds-section__inner--centered {
  text-align: center;
}

/* Centered hero-inner with the default 760 px cap — mirrors the most common
   marketing hero pattern (blog index, contact). Combines margin + center
   alignment in one modifier rather than two classes. */
.ds-section__inner.ds-section__inner--hero {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}

/* Long-form text columns — blog article bodies (prose-body) and the legal
   pages (impressum, datenschutz, agb, widerruf). Deliberately uncapped:
   running text takes the full width of the section column (the 1200 px
   .ds-section__inner base), and the distance to the edge comes from the
   section padding alone. A reading-width cap left a wide empty strip beside
   every paragraph while the tables next to it ran to the edge.
   tests/test_design_gates.py keeps any max-width off these columns and off
   prose paragraphs, lists and headings. */
.ds-section__inner.ds-section__inner--prose-body,
.ds-section__inner.ds-section__inner--legal {
  margin: 0 auto;
}

/* Narrow panel — 560 px cap for single-focus confirmation/empty-state layouts. */
.ds-section__inner.ds-section__inner--narrow {
  max-width: 560px;
  margin: 0 auto;
}

/* Section variant that vertically centers its content within a 60 vh minimum
   height (used on the contact-thanks single-state screen). */
.ds-section--vh60 {
  min-height: 60vh;
  display: flex;
  align-items: center;
}

/* Error pages: the layout-shell body is flex:1 — let the single section
   absorb that height and center its content so no dead band remains
   between the tinted section and the footer at tall viewports. */
.ds-section--fill {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Error pages (the only users of .ds-section--fill) place the status badge
   directly above the headline. Give the badge breathing room so it does not
   crowd the title — applies to every error/status page at once. */
.ds-section--fill .ds-eyebrow {
  margin-bottom: var(--ds-space-5);
}

.ds-section--fill .ds-section__title {
  font-size: var(--ds-text-hero-standard);
  letter-spacing: var(--ds-tracking-display);
}

.ds-section__inner .ds-section__lead {
  margin-left: auto;
  margin-right: auto;
}

/* Button row used inside .ds-section__inner for error pages and other
   centered CTA layouts. Token-based gap, wraps on narrow viewports. */
.ds-button-group {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ds-space-3, 0.75rem);
  margin-top: var(--ds-space-4, 1rem);
}

/* Single-column FAQ stack — blog articles render their FAQ accordion
   inside the prose column; the landing page uses .ds-faq-grid instead. */
.ds-faq-stack {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-6);
}

/* FAQ accordion (custom.js toggles .is-open and the answer's max-height).
   White cards on the page; the open item takes a violet edge and a deeper
   shadow, its "+" turns into a gradient "×". The ring sits inset, because
   the item clips its overflow and an outset ring would be cut off. */
.ds-op-faq-item {
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-sm);
  overflow: hidden;
}

.ds-op-faq-item:hover {
  border-color: var(--ds-border-strong);
}

.ds-op-faq-item.is-open {
  border-color: rgba(var(--ds-purple-rgb), 0.32);
  box-shadow: var(--ds-shadow-md);
}

.ds-op-faq-question {
  width: 100%;
  background: transparent;
  border: none;
  padding: var(--ds-space-4-5) var(--ds-space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Keeps the "+" clear of a question that wraps to the full width. */
  gap: var(--ds-space-4);
  text-align: left;
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-bold);
  letter-spacing: var(--ds-tracking-snug);
  color: var(--ds-ink);
  cursor: pointer;
}

.ds-op-faq-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--ds-radius-full);
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-blue);
  font-size: var(--ds-text-body);
  transition:
    transform var(--ds-dur-base) var(--ds-ease-out),
    background-color var(--ds-dur-fast) var(--ds-ease-standard),
    color var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-op-faq-item.is-open .ds-op-faq-icon {
  transform: rotate(45deg);
  background: var(--ds-gradient-icon-tile);
  color: var(--ds-white);
}

.ds-op-faq-answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s var(--ds-ease-out);
}

.ds-op-faq-answer p {
  padding: 0 var(--ds-space-5) var(--ds-space-5);
  margin: 0;
  font-size: 0.9375rem;
  line-height: var(--ds-lh-prose);
  color: var(--ds-ink-soft);
}

.ds-op-faq-question:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 3px var(--ds-blue);
  border-radius: var(--ds-radius-md);
}
.ds-op-faq-question:hover {
  background: var(--ds-surface-tint);
}

/* ============================================================
   Graphics, badges, decorative patterns
   ============================================================ */
@keyframes opSparkleTwinkle {
  0%,
  100% {
    opacity: 0.4;
    transform: scale(0.9) rotate(0deg);
  }
  50% {
    opacity: 1;
    transform: scale(1.05) rotate(10deg);
  }
}

/* ============================================================
   Block A — Design-System Unification
   Card shadows, eyebrow shape and headline typography unified
   across marketing, app and error surfaces. The former
   .btn.btn-primary gradient layer was deleted; every submit
   button (incl. the contact form) now renders through the
   ds-button ds-button--primary classes.
   ============================================================ */

/* --- Section-title typography consistency --- */
.ds-section__title {
  font-family: var(--ds-font-display);
} /* --- Subtitle typography consistency --- */
.ds-section__lead {
  font-family: var(--ds-font-body);
  font-weight: var(--ds-fw-regular);
}

/* ========================================================================
   Block B — UX-Polish additions (toasts, skeletons, empty-states,
   tooltips, form-validation, onboarding tour, htmx indicator).
   ======================================================================== */

/* === Toast notifications =============================================== */
.ls-toast-container {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: var(--ds-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  max-width: 360px;
  width: calc(100vw - 40px);
  pointer-events: none;
}

.ls-toast {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-3);
  padding: var(--ds-space-3-5) var(--ds-space-4);
  background: rgba(var(--ds-ink-rgb), 0.96);
  color: var(--ds-white);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-toast);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(var(--ds-white-rgb), 0.06);
  overflow: hidden;
  pointer-events: auto;
  opacity: 0;
  transform: translateX(24px);
  transition:
    opacity 0.24s ease,
    transform 0.24s ease;
  font-family: var(--ds-font-body);
}

.ls-toast::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 4px;
  background: var(--ds-blue);
}

.ls-toast-in {
  opacity: 1;
  transform: translateX(0);
}

.ls-toast-leaving {
  opacity: 0;
  transform: translateX(24px);
}

.ls-toast-icon {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: var(--ds-radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.05rem;
  background: rgba(var(--ds-white-rgb), 0.08);
  color: var(--ds-white);
}

.ls-toast-success .ls-toast-icon {
  background: rgba(var(--ds-success-bright-rgb), 0.22);
  color: var(--ds-success-on-dark);
}
.ls-toast-error .ls-toast-icon {
  background: rgba(var(--ds-red-rgb), 0.22);
  color: var(--ds-danger-on-dark);
}
.ls-toast-warning .ls-toast-icon {
  background: rgba(var(--ds-warn-bright-rgb), 0.22);
  color: var(--ds-warn-on-dark);
}
.ls-toast-info .ls-toast-icon {
  background: rgba(var(--ds-blue-rgb), 0.25);
  color: var(--ds-info-on-dark);
}

.ls-toast-body {
  flex: 1;
  min-width: 0;
  padding-top: var(--ds-space-1);
}

.ls-toast-message {
  display: block;
  font-size: var(--ds-text-body-sm);
  line-height: 1.45;
  color: rgba(var(--ds-white-rgb), 0.94);
  word-break: break-word;
}

.ls-toast-close {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border: 0;
  background: transparent;
  color: rgba(var(--ds-white-rgb), 0.55);
  border-radius: var(--ds-radius-xs);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--ds-text-lg);
  transition:
    background 0.18s,
    color 0.18s;
}

.ls-toast-close:hover {
  background: rgba(var(--ds-white-rgb), 0.08);
  color: var(--ds-white);
}

.ls-toast-close:focus-visible {
  background: rgba(var(--ds-white-rgb), 0.08);
  color: var(--ds-white);
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--ds-white-rgb), 0.35);
}

/* === Skeleton loaders ================================================== */
.ls-skel {
  display: block;
  border-radius: var(--ds-radius-xs);
  background: linear-gradient(
    90deg,
    rgba(var(--ds-ink-rgb), 0.08) 0%,
    rgba(var(--ds-ink-rgb), 0.14) 50%,
    rgba(var(--ds-ink-rgb), 0.08) 100%
  );
  background-size: 200% 100%;
  animation: lsSkelShimmer 1.4s ease-in-out infinite;
}

.ls-skel-line {
  height: 12px;
  width: 100%;
}

/* HTMX live-count indicator — only visible during htmx-request. */
.ls-skel-count {
  display: none;
  margin-top: var(--ds-space-1);
  max-width: 160px;
}

.htmx-request .ls-skel-count,
.htmx-request.ls-skel-count {
  display: block;
}

@keyframes lsSkelShimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

/* === Inline form validation ============================================ */
/* !important sanctioned: the JS-applied validation state must outrank the
   :focus border/shadow of every input system while the user is typing. */
.ls-field-invalid {
  border-color: var(--ds-danger) !important;
  box-shadow: var(--ds-shadow-field-danger) !important;
}

.ls-field-valid {
  border-color: var(--ds-success) !important;
  box-shadow: var(--ds-shadow-field-success) !important;
}

/* While the field has keyboard focus the opaque ring takes over again, so
   the state colour never costs the control its 3:1 focus indicator. */
.ls-field-invalid:focus-visible {
  box-shadow: var(--ds-shadow-focus-danger) !important;
}

.ls-field-valid:focus-visible {
  box-shadow: var(--ds-shadow-focus-success) !important;
}

.ls-field-error {
  display: flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  margin-top: var(--ds-space-1-5);
  color: var(--ds-danger);
  font-size: var(--ds-text-body-sm);
  line-height: 1.3;
}

.ls-field-error i {
  flex-shrink: 0;
  font-size: var(--ds-text-body);
}

.ls-field-ok {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ds-success);
  font-size: 1.05rem;
  pointer-events: none;
}

.ls-validate-wrap {
  position: relative;
  display: block;
}

/* === Onboarding tour =================================================== */
.ls-tour-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--ds-z-tour);
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.22s ease;
}

.ls-tour-overlay.ls-tour-leaving {
  opacity: 0;
}

.ls-tour-backdrop {
  position: absolute;
  inset: 0;
  background: var(--ds-backdrop);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  pointer-events: auto;
}

.ls-tour-highlight {
  position: absolute;
  border-radius: var(--ds-radius-md);
  box-shadow:
    0 0 0 4px rgba(var(--ds-white-rgb), 0.9),
    0 0 0 9999px var(--ds-backdrop);
  transition:
    top 0.25s ease,
    left 0.25s ease,
    width 0.25s ease,
    height 0.25s ease;
  pointer-events: none;
  display: none;
}

.ls-tour-tooltip {
  position: absolute;
  width: 360px;
  max-width: calc(100vw - 40px);
  padding: 22px 22px 18px;
  background: var(--ds-white);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-tour);
  pointer-events: auto;
  font-family: var(--ds-font-body);
  transition:
    top 0.25s ease,
    left 0.25s ease;
}

/* Overrides the JS-driven absolute top/left coordinates set per-step so the
   centered-mode tooltip lands at viewport mid-point regardless of scroll. */
.ls-tour-tooltip-center {
  top: 50% !important;
  left: 50% !important;
  transform: translate(-50%, -50%);
}

.ls-tour-step {
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-blue);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin-bottom: var(--ds-space-2);
}

.ls-tour-title {
  margin: 0 0 var(--ds-space-2);
  font-size: 1.2rem;
  font-weight: var(--ds-fw-black);
  color: var(--ds-ink);
  letter-spacing: -0.01em;
}

.ls-tour-body {
  margin: 0 0 var(--ds-space-4-5);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
  line-height: var(--ds-lh-base);
}

.ls-tour-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--ds-space-3);
}

.ls-tour-skip {
  border: 0;
  background: transparent;
  color: var(--ds-ink-mute);
  font-weight: var(--ds-fw-semi);
  font-size: var(--ds-text-body-sm);
  cursor: pointer;
  padding: var(--ds-space-2) var(--ds-space-3);
  border-radius: var(--ds-radius-xs);
  transition:
    background 0.18s,
    color 0.18s;
}

.ls-tour-skip:hover,
.ls-tour-skip:focus {
  background: rgba(var(--ds-ink-rgb), 0.08);
  color: var(--ds-ink);
  outline: none;
}

/* The tour is a modal layer with a focus trap, so the ring is the only cue
   telling a keyboard user which of the two buttons is selected — the hover
   styling above is identical for both and cannot carry that job. */
.ls-tour-skip:focus-visible {
  box-shadow: var(--ds-shadow-focus);
}

.ls-tour-next {
  border: 0;
  padding: var(--ds-space-2-5) var(--ds-space-5);
  background: var(--ds-blue);
  color: var(--ds-white);
  font-weight: var(--ds-fw-semi);
  font-size: var(--ds-text-body-sm);
  border-radius: var(--ds-radius-sm);
  cursor: pointer;
  box-shadow: var(--ds-shadow-sm);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}

.ls-tour-next:hover,
.ls-tour-next:focus {
  background: var(--ds-blue-dark);
  box-shadow: var(--ds-shadow-primary-hover);
  outline: none;
}

/* Same reasoning as the skip button. The face carries the brand gradient, so
   the ring has to be the on-brand variant; the lift shadow is repeated because
   box-shadow does not merge across rules. */
.ls-tour-next:focus-visible {
  box-shadow:
    0 10px 20px rgba(var(--ds-blue-rgb), 0.3),
    var(--ds-shadow-focus-on-brand);
}

/* Cursor-follow glow layer. The element is created by motion.js
   (cursorFollowGlow) and appended to a dark container; only its opacity and
   GSAP transform are set from script, so the appearance stays token-driven
   here instead of in an inline style string. */
.ls-cursor-glow {
  position: absolute;
  top: 0;
  left: 0;
  width: 400px;
  height: 400px;
  margin: -200px 0 0 -200px;
  background: radial-gradient(
    circle,
    rgba(var(--ds-blue-glow-rgb), 0.25),
    transparent 60%
  );
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transition: opacity 300ms ease;
}

/* === Tooltips ========================================================== */
.ls-tooltip {
  position: absolute;
  top: -9999px;
  left: -9999px;
  z-index: 1100;
  max-width: 240px;
  padding: var(--ds-space-2) var(--ds-space-3);
  background: var(--ds-ink);
  color: var(--ds-white);
  border-radius: var(--ds-radius-xs);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-medium);
  line-height: 1.4;
  font-family: var(--ds-font-body);
  box-shadow: var(--ds-shadow-tooltip);
  opacity: 0;
  transform: translateY(-2px);
  transition:
    opacity 0.16s ease,
    transform 0.16s ease;
  pointer-events: none;
  white-space: normal;
}

.ls-tooltip-visible {
  opacity: 1;
  transform: translateY(0);
}

[data-tooltip] {
  cursor: help;
}

[data-tooltip].btn,
[data-tooltip] button,
[data-tooltip] a {
  cursor: inherit;
}

/* === HTMX loading indicator polish ===================================== */
.htmx-request,
.htmx-request.htmx-request {
  opacity: 0.6;
  animation: lsHtmxPulse 1.2s ease-in-out infinite;
  transition: opacity 0.18s ease;
}

.htmx-indicator {
  opacity: 0;
  transition: opacity 0.18s ease;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

@keyframes lsHtmxPulse {
  0%,
  100% {
    opacity: 0.65;
  }
  50% {
    opacity: 0.92;
  }
}

/* --- Disabled form controls (globally; affects marketing + app + auth) */
input:disabled,
textarea:disabled,
select:disabled,
.form-control:disabled,
.form-select:disabled,
input[readonly],
textarea[readonly],
.form-control[readonly] {
  background-color: var(--ds-surface-tint);
  color: var(--ds-ink-mute);
  border-color: var(--ds-surface-3);
  cursor: not-allowed;
  opacity: 0.7;
  box-shadow: none;
}
/* Disabled state unification — generic fallback for non-ds buttons. */
button:disabled,
[type="submit"]:disabled {
  cursor: not-allowed;
  opacity: 0.55;
  pointer-events: none;
  box-shadow: none;
  transform: none;
}
/* --- Keyboard focus rings. Bootstrap & Crispy defaults are disabled in
       many places; this restores a consistent 3-px ring in brand blue.
       The colour is the OPAQUE --ds-blue (7.5:1 on white), never the
       translucent --ds-border-strong: that token composites to #c8d8ef and
       measures 1.44:1, far below the 3:1 that WCAG 1.4.11 requires of a
       focus indicator. Controls carrying their own --ds-shadow-focus ring
       were unaffected; every other link and button fell back to here and
       had, in practice, no visible focus at all. */
.blog-card:focus-visible,
.ds-op-faq-item:focus-visible {
  outline: 3px solid var(--ds-blue);
  outline-offset: 3px;
}
button:focus-visible,
[type="submit"]:focus-visible,
[type="button"]:focus-visible {
  outline: 3px solid var(--ds-blue);
  outline-offset: 3px;
}

a:focus-visible {
  outline: 2px solid var(--ds-blue);
  outline-offset: 2px;
  border-radius: var(--ds-radius-xs);
}

/* Same ring on dark surfaces, in the on-dark accent. Brand blue measures
   6.67:1 against the light page but only 2.87:1 against the navy stage,
   so the dark marketing sections and the full footer would sit below the
   3:1 floor of WCAG 1.4.11 on exactly the links that carry no ring of their
   own (footer columns, links inside dark bands). The accent measures
   9.82:1 on the navy. */
.ds-section--dark a:focus-visible,
.ds-section--dark button:focus-visible,
.ds-site-footer a:focus-visible,
.ds-site-footer button:focus-visible,
.ds-cta-band a:focus-visible,
.ds-home-hero a:focus-visible,
.ds-page-hero a:focus-visible,
.ds-sticky-cta a:focus-visible,
.ds-sticky-cta button:focus-visible {
  outline-color: var(--ds-ink-on-dark-accent);
}

/* Table rows receive focus-within (users tab through embedded links) */
.table tbody tr:focus-within {
  background: var(--ds-surface-tint);
  outline: 2px solid var(--ds-blue);
  outline-offset: -2px;
}

/* ============================================================
   Utility classes — replace inline font-size/color overrides
   that were previously scattered across templates. Define once,
   reuse wherever a size/weight override is genuinely needed.
   ============================================================ */

/* Standardized icon size for alert / hint panels. */
.alert-icon-lg {
  font-size: 1.5rem;
  flex-shrink: 0;
}
/* ============================================================
   Shared card hover — blog cards and FAQ items take their
   transition from --ds-card-transition; blog cards lift by
   --ds-card-lift like every link card (a.ds-card).
   ============================================================ */
.blog-card,
.ds-op-faq-item {
  transition: var(--ds-card-transition);
}
/* ============================================================
   Article metadata strip (blog article hero) — date / reading-time /
   pillar badge displayed as an icon-led row.
   ============================================================ */
.article-metadata {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2) var(--ds-space-5);
  margin-top: var(--ds-space-5);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
}

.article-metadata-item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  line-height: 1;
  white-space: nowrap;
}

.article-metadata-item > i {
  font-size: var(--ds-text-body);
  color: var(--ds-ink-mute);
}

.article-metadata-badge {
  padding: var(--ds-space-0-5) var(--ds-space-2);
  border-radius: var(--ds-radius-full);
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-neutral-tint-text);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
}

/* ============================================================
   Mobile polish — remaining responsive gaps flagged in the audit.
   ============================================================ */

/* FAQ accordion tap targets must hit the 44-px mobile minimum. */
.ds-op-faq-question {
  min-height: 44px;
}

/* ============================================================
   Page-level fade-in + toast / flash-message animations.
   Defined last so they can reference the motion tokens without
   needing overrides.
   ============================================================ */

@keyframes ls-page-fade-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* App-shell pages fade in when they load — subtle 300 ms entrance
   so every navigation within the app feels intentional. Marketing
   pages get no page fade: their entrance motion is the .ds-reveal
   scroll reveal below (custom.js, every page) plus, on the home page
   only, the GSAP stagger home.js runs on .ds-stagger-child-gsap.
   fill-mode is `backwards` (not `both`): a forwards-filled transform
   keeps computing as a transform after the animation ends, which turns
   the container into the containing block for every position:fixed
   descendant (mobile filter sheet, modals) and pins them to the page
   instead of the viewport. */
.app-shell .app-container {
  animation: ls-page-fade-in 340ms var(--ds-ease-standard) backwards;
}

/* ============================================================
   Global scroll-reveal + stagger system
   Driven by custom.js IntersectionObserver. Any element with class
   .ds-reveal starts hidden and animates into place when it scrolls
   into view. Stagger delays are injected inline by the JS observer for
   children of a .ds-stagger-parent.

   Most children of a .ds-stagger-parent-gsap grid on the home page (all
   but the FAQ items) carry .ds-reveal too, so this transition and the
   GSAP tween in home.js both run on them.
   ============================================================ */
.ds-reveal {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity var(--ds-dur-slow) var(--ds-ease-out),
    transform var(--ds-dur-slow) var(--ds-ease-out);
}

.ds-reveal.is-revealed {
  opacity: 1;
  transform: none;
}

/* One-time entrance for the hero mockup on page load: it rises into place
   while the text, the LCP element, renders at once. The reduced-motion
   block at the end of the file collapses the animation. */
@keyframes ds-rise {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ============================================================
   Icon micro-interactions
   Icons next to CTAs or inline links nudge slightly when the
   parent is hovered. Nothing flashy — 2 px translate for arrows,
   subtle rotate for gear / chevron icons.
   ============================================================ */
a .bi-arrow-right,
button .bi-arrow-right {
  display: inline-block;
  transition: transform var(--ds-dur-fast) var(--ds-ease-standard);
}

a:hover > .bi-arrow-right,
button:hover > .bi-arrow-right {
  transform: translateX(3px);
}

a:hover > .bi-arrow-left {
  transform: translateX(-3px);
}

.bi-arrow-left {
  display: inline-block;
  transition: transform var(--ds-dur-fast) var(--ds-ease-standard);
}

/* ============================================================
   Inline link underline slide
   Links in FAQ answers get a subtle underline-slide on hover; article
   and legal links keep the permanent underline of .ds-prose.
   ============================================================ */
.ds-op-faq-answer a {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition:
    background-size 0.35s var(--ds-ease-standard),
    color var(--ds-dur-fast) var(--ds-ease-standard);
  text-decoration: none;
}

.ds-op-faq-answer a:hover {
  background-size: 100% 1px;
  color: var(--ds-blue);
}

/* ========================================================================
   LANDING PAGE (lp-*) — Full-bleed landing page on home.html
   Dashboard language (palette, typography, orbs, gradients) preserved.
   ======================================================================== */

.ds-shell {
  background: var(--ds-white);
  /* `overflow-x: clip` (set globally on html/body above) is preferred over
       `hidden` because hidden auto-promotes overflow-y to auto, which makes
       the body itself the scroll container and breaks `position: sticky` on
       the .ds-site-header. The global clip rule already handles horizontal
       overflow for decorative orbs, so this scope only sets the surface. */
}

.ds-wrap {
  position: relative;
  z-index: 1;
  /* Explicit width: inside a flex parent (e.g. .ds-hero--dramatic) the auto
       inline margins suppress cross-axis stretching, so without it the wrap
       sizes to fit-content — a nowrap child (hero ticker marquee) then
       inflates it past the viewport and the hero clips below 1280px. */
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--ds-gutter);
}

/* Constrained-width variant for staff-only metrics pages (960 px reading
   column, narrower than the default 1280 px full-width wrap). */
.ds-wrap--metrics {
  max-width: 960px;
}

@media (max-width: 991px) {
  .ds-hero {
    min-height: auto;
  }
}

/* ----- Section headings -----
   Eyebrow pill, H2 with a short brand-gradient accent line under it, and a
   lead. A heading block, not running text, so the 780px cap is a layout
   width. --start aligns the block left for split layouts. On the dark
   stage the colours switch to the on-dark set. */
.ds-section-head {
  max-width: 780px;
  margin: 0 auto var(--ds-space-12);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ds-space-4);
}
.ds-section-head h2 {
  font-size: var(--ds-text-section);
  font-weight: var(--ds-fw-black);
  color: var(--ds-ink);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0;
}
.ds-section-head h2::after {
  content: "";
  display: block;
  width: 56px;
  height: 4px;
  margin: var(--ds-space-5) auto 0;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-brand);
}
.ds-section-head p {
  font-size: var(--ds-text-lead);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-soft);
  margin: 0;
}
/* The eyebrow is a <p> inside the head; it must not take the lead style. */
.ds-section-head p.ds-eyebrow {
  font-size: var(--ds-text-eyebrow);
  line-height: 1.2;
  color: var(--ds-blue-dark);
}
.ds-section-head--start {
  align-items: flex-start;
  text-align: left;
  margin-inline: 0;
}
.ds-section-head--start h2::after {
  margin-left: 0;
}
.ds-section--dark .ds-section-head h2 {
  color: var(--ds-ink-on-dark-strong);
}
.ds-section--dark .ds-section-head p {
  color: var(--ds-ink-on-dark-body);
}
.ds-section--dark .ds-section-head p.ds-eyebrow {
  color: var(--ds-ink-on-dark-accent);
}

/* ----- FAQ ----- */
.ds-faq-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ds-space-3) var(--ds-space-6);
  align-items: start;
}
@media (max-width: 767px) {
  .ds-faq-grid {
    grid-template-columns: 1fr;
  }
}

/* ================================================================
   LP Hero — Dark Aesthetic (Iteration 2)
   Layered atmosphere, glassmorphic mockup, mini-stats, ticker, scroll cue.
   Overrides .ds-section--atmo background on the dramatic hero.
   ================================================================ */

@keyframes ds-hero-spin {
  from {
    transform: translate(-50%, -50%) rotate(0deg);
  }
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}
@keyframes ds-orb-drift {
  0% {
    transform: translate(0, 0) scale(1);
  }
  50% {
    transform: translate(3%, -2%) scale(1.04);
  }
  100% {
    transform: translate(-2%, 3%) scale(0.98);
  }
}
@keyframes ds-particle-float {
  0% {
    transform: translateY(110vh) scale(0.8);
    opacity: 0;
  }
  10% {
    opacity: 0.7;
  }
  85% {
    opacity: 0.4;
  }
  100% {
    transform: translateY(-20vh) scale(1.15);
    opacity: 0;
  }
}
@keyframes ds-beam-slide {
  0%,
  100% {
    transform: translateY(-10%) rotate(6deg);
    opacity: 0;
  }
  50% {
    transform: translateY(10%) rotate(6deg);
    opacity: 0.55;
  }
}
@keyframes ds-beam-slide-alt {
  0%,
  100% {
    transform: translateY(10%) rotate(-8deg);
    opacity: 0;
  }
  50% {
    transform: translateY(-10%) rotate(-8deg);
    opacity: 0.55;
  }
}

@keyframes ds-gradient-shimmer {
  0%,
  100% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
}
@keyframes ds-float-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-7px);
  }
}

@keyframes ds-ticker-scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}
@keyframes ds-scroll-dive {
  0% {
    top: -60%;
    opacity: 0;
  }
  20% {
    opacity: 1;
  }
  100% {
    top: 110%;
    opacity: 0;
  }
}
/* ================================================================
   Gradient icon tile — the one icon voice of the site: a white glyph on
   the blue-to-violet tile (6.67 / 6.37:1) with a soft blue shadow. Card
   icons, link-card icons and the feature tiles all share it; size
   modifiers live with their components.
   ================================================================ */
.ds-card .ds-card__icon,
.ds-link-card__icon,
.ds-icon-tile {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--ds-radius-sm);
  background: var(--ds-gradient-icon-tile);
  color: var(--ds-white);
  font-size: 20px;
  line-height: 1;
  box-shadow: var(--ds-shadow-icon-tile);
}

/* Small tile for dense rows (fact strip, field lists, footer facts). */
.ds-icon-tile--sm {
  width: 32px;
  height: 32px;
  border-radius: var(--ds-radius-xs);
  font-size: 16px;
}

/* Large tile for timeline steps and bento highlights. */
.ds-icon-tile--lg {
  width: 56px;
  height: 56px;
  border-radius: var(--ds-radius-md);
  font-size: 24px;
  box-shadow: var(--ds-shadow-icon-tile-lg);
}

/* ============================================================================
 * DS COMPONENTS — Phase 2 Unification
 * The unified .ds-* namespace. The former legacy namespaces (.lp-*,
 * .app-prem-*, etc.) were consolidated into it during the Phase 2 migration.
 * ========================================================================== */

/* === Layout Shells === */

.ds-layout-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--ds-surface-tint);
  color: var(--ds-ink);
  font-family: var(--ds-font-body);
  font-size: var(--ds-text-body);
  line-height: var(--ds-lh-base);
}

.ds-layout-shell__body {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.ds-layout-shell--app .ds-layout-shell__body {
  padding: 0;
}

/* App pages sit on the flat tint; cards and tables are the white islands. */
body.ds-layout-shell--app,
.ds-layout-shell--app {
  background: var(--ds-surface-tint);
}

/* ============================================================================
 * App-Sidebar — sticky left rail on app pages
 * Desktop (>= 1200px): 248px expanded; click-to-collapse to 64px (icons only)
 * Tablet (768-1199): 64px icon-rail by default; hover expands as overlay
 * Mobile (< 768): slide-over drawer via header burger; backdrop overlays main
 * ============================================================================ */

.ds-app-sidebar {
  position: fixed;
  top: var(--ds-app-header-h);
  left: 0;
  bottom: 0;
  width: var(--ds-sidebar-width);
  background: var(--ds-surface);
  border-right: 1px solid var(--ds-border);
  z-index: 90;
  overflow-x: hidden;
  overflow-y: auto;
  transition:
    width var(--ds-dur-base) var(--ds-ease-out),
    transform var(--ds-dur-base) var(--ds-ease-out);
  scrollbar-width: thin;
  scrollbar-color: var(--ds-surface-4) transparent;
}

.ds-app-sidebar::-webkit-scrollbar {
  width: 6px;
}
.ds-app-sidebar::-webkit-scrollbar-thumb {
  background: var(--ds-surface-4);
  border-radius: var(--ds-radius-xs);
}

.ds-app-sidebar__inner {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding: var(--ds-space-4) var(--ds-space-3);
  gap: var(--ds-space-3);
}

/* Nav */
.ds-app-sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-0-5);
  flex: 1 1 auto;
}

.ds-app-sidebar__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  min-height: 40px;
  padding: 0 var(--ds-space-3);
  border-radius: var(--ds-radius-sm);
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  text-decoration: none;
  transition:
    background var(--ds-dur-fast) var(--ds-ease-out),
    color var(--ds-dur-fast) var(--ds-ease-out);
  white-space: nowrap;
}
.ds-app-sidebar__icon {
  flex-shrink: 0;
  width: 20px;
  text-align: center;
  font-size: 18px;
  color: var(--ds-ink-mute);
  transition: color var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-app-sidebar__label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ds-app-sidebar__item:hover {
  background: var(--ds-surface-tint);
  color: var(--ds-ink);
}
.ds-app-sidebar__item:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* The current page: the neutral tint, semibold label and a short
   gradient bar along the left edge, the app's one brand accent in the
   navigation. */
.ds-app-sidebar__item.is-active {
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-neutral-tint-text);
  font-weight: var(--ds-fw-semi);
}
.ds-app-sidebar__item.is-active::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--ds-space-2);
  bottom: var(--ds-space-2);
  width: 3px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-cta);
}
.ds-app-sidebar__item.is-active .ds-app-sidebar__icon {
  color: var(--ds-blue);
}

/* Secondary items differ by colour only — the size matches the base item. */
.ds-app-sidebar__item--secondary {
  color: var(--ds-ink-mute);
}

/* Collapsed-sidebar state: center icon-only items inside the 64px rail.
   The labels further down the cascade keep `opacity: 0` (used in transition)
   but stay in flow, so we must collapse them out of the flex layout here
   — otherwise their `flex: 1 1 auto` would swallow the free space and pin
   the icon to the left of the item, breaking `justify-content: center`. */
body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar__item,
body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar__collapse {
  justify-content: center;
  padding-inline: 0;
  gap: 0;
}

body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar__label,
body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar__collapse-label {
  flex: 0 0 0;
  width: 0;
  overflow: hidden;
}

/* Group label above a block of items ("Suchen", "Konto"). */
.ds-app-sidebar__section-label {
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-fw-semi);
  letter-spacing: var(--ds-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ds-ink-mute);
  padding: var(--ds-space-4) var(--ds-space-3) var(--ds-space-1-5);
  white-space: nowrap;
}

/* Mobile-only marketing block — hidden on tablet/desktop */
.ds-app-sidebar__mobile-marketing {
  display: none;
}

/* Footer: wallet card + collapse toggle */
.ds-app-sidebar__footer {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  flex-shrink: 0;
  margin-top: auto;
  padding-top: var(--ds-space-3);
  border-top: 1px solid var(--ds-border-subtle);
}

/* The wallet card is the single credit display on desktop: the balance in
   tabular figures and a compact "Nachkaufen" control. */
.ds-app-sidebar__wallet {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--ds-space-2) var(--ds-space-3);
  padding: var(--ds-space-3);
  border-radius: var(--ds-radius-md);
  background: var(--ds-surface-tint);
  border: 1px solid var(--ds-border);
  color: var(--ds-ink);
  text-decoration: none;
}
a.ds-app-sidebar__wallet:hover {
  color: var(--ds-ink);
  border-color: var(--ds-border-strong);
}
.ds-app-sidebar__wallet:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}
.ds-app-sidebar__wallet .bi-coin {
  font-size: var(--ds-text-lg);
  color: var(--ds-blue);
  flex-shrink: 0;
}
.ds-app-sidebar__wallet-text {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-2);
  min-width: 0;
  flex: 1;
}
.ds-app-sidebar__wallet-caption {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  line-height: 1.2;
}
.ds-app-sidebar__wallet-value {
  white-space: nowrap;
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
  line-height: 1.3;
}
.ds-app-sidebar__wallet-label {
  display: inline-flex;
  align-items: center;
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-3);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-strong);
  border-radius: var(--ds-radius-xs);
}
a.ds-app-sidebar__wallet:hover .ds-app-sidebar__wallet-label {
  border-color: var(--ds-border-control);
}
.ds-app-sidebar__wallet.is-low {
  background: var(--ds-warn-tint-bg);
  border-color: transparent;
}
.ds-app-sidebar__wallet.is-low .bi-coin,
.ds-app-sidebar__wallet.is-low .ds-app-sidebar__wallet-value {
  color: var(--ds-warn-tint-text);
}
.ds-app-sidebar__wallet.is-critical {
  background: var(--ds-danger-tint-bg);
  border-color: transparent;
}
.ds-app-sidebar__wallet.is-critical .bi-coin,
.ds-app-sidebar__wallet.is-critical .ds-app-sidebar__wallet-value {
  color: var(--ds-danger-tint-text);
}

.ds-app-sidebar__logout-form {
  margin: 0;
  padding: 0;
}

.ds-app-sidebar__logout {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  width: 100%;
  min-height: 40px;
  padding: 0 var(--ds-space-3);
  border: 0;
  background: transparent;
  border-radius: var(--ds-radius-sm);
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  text-align: left;
  cursor: pointer;
  transition:
    background var(--ds-dur-fast) var(--ds-ease-out),
    color var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-app-sidebar__logout:hover {
  background: var(--ds-surface-tint);
  color: var(--ds-ink);
}
.ds-app-sidebar__logout:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

.ds-app-sidebar__collapse {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2-5);
  min-height: 36px;
  padding: 0 var(--ds-space-3);
  border: 0;
  background: transparent;
  border-radius: var(--ds-radius-sm);
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-medium);
  cursor: pointer;
  transition:
    background var(--ds-dur-fast) var(--ds-ease-out),
    color var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-app-sidebar__collapse:hover {
  background: var(--ds-surface-tint);
  color: var(--ds-ink);
}
.ds-app-sidebar__collapse:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}
.ds-app-sidebar__collapse .bi {
  font-size: var(--ds-text-body);
  transition: transform var(--ds-dur-base) var(--ds-ease-out);
}
body.ds-sidebar-collapsed .ds-app-sidebar__collapse .bi {
  transform: rotate(180deg);
}

/* Main-content offset (desktop default). Only the left side clears the
   fixed sidebar; the right side keeps a small gutter instead of mirroring
   the sidebar width. The former symmetric "phantom space" centered the
   content but squeezed it so far that the lead tables clipped their score
   and action columns on every common desktop width — content-first wins,
   and the minor horizontal shift on sidebar toggle is the accepted
   trade-off. */
@media (min-width: 1200px) {
  body.ds-layout-shell--app .ds-layout-shell__body,
  body.ds-layout-shell--app .ds-site-footer {
    padding-inline: var(--ds-sidebar-width) var(--ds-space-6);
  }
  body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar {
    width: var(--ds-sidebar-width-collapsed);
  }
  body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar__label,
  body.ds-layout-shell--app.ds-sidebar-collapsed .ds-app-sidebar__wallet-text,
  body.ds-layout-shell--app.ds-sidebar-collapsed
    .ds-app-sidebar__collapse-label,
  body.ds-layout-shell--app.ds-sidebar-collapsed
    .ds-app-sidebar__section-label {
    opacity: 0;
    pointer-events: none;
  }
}

/* Tablet: icon-rail by default, hover expands as overlay */
@media (min-width: 768px) and (max-width: 1199px) {
  body.ds-layout-shell--app .ds-app-sidebar {
    width: var(--ds-sidebar-width-collapsed);
  }
  body.ds-layout-shell--app .ds-layout-shell__body,
  body.ds-layout-shell--app .ds-site-footer {
    /* Left clears the 64px icon-rail; right keeps a small gutter so
           the constrained tablet width goes to content, not symmetry. */
    padding-inline: var(--ds-sidebar-width-collapsed) var(--ds-space-4);
  }
  body.ds-layout-shell--app .ds-app-sidebar__label,
  body.ds-layout-shell--app .ds-app-sidebar__wallet-text,
  body.ds-layout-shell--app .ds-app-sidebar__collapse-label,
  body.ds-layout-shell--app .ds-app-sidebar__section-label {
    opacity: 0;
    pointer-events: none;
  }
  body.ds-layout-shell--app .ds-app-sidebar:hover {
    width: var(--ds-sidebar-width);
    box-shadow: var(--ds-shadow-lg);
  }
  body.ds-layout-shell--app .ds-app-sidebar:hover .ds-app-sidebar__label,
  body.ds-layout-shell--app .ds-app-sidebar:hover .ds-app-sidebar__wallet-text,
  body.ds-layout-shell--app
    .ds-app-sidebar:hover
    .ds-app-sidebar__collapse-label,
  body.ds-layout-shell--app
    .ds-app-sidebar:hover
    .ds-app-sidebar__section-label {
    opacity: 1;
    pointer-events: auto;
  }
  body.ds-layout-shell--app .ds-app-sidebar__collapse {
    display: none;
  }
}

/* Mobile: slide-over drawer */
@media (max-width: 767px) {
  body.ds-layout-shell--app .ds-app-sidebar {
    width: 280px;
    transform: translateX(-100%);
    box-shadow: var(--ds-shadow-lg);
    /* A translated drawer stays in the tab order: from the burger, keyboard
       focus walked into nine invisible nav links parked at x=-268 with no
       visible indicator anywhere on screen. `visibility: hidden` takes the
       closed drawer out of the tab order; it is delayed by the slide duration
       so the closing animation still plays, and reverts instantly on open. */
    visibility: hidden;
    transition:
      width var(--ds-dur-base) var(--ds-ease-out),
      transform var(--ds-dur-base) var(--ds-ease-out),
      visibility 0s linear var(--ds-dur-base);
  }
  body.ds-layout-shell--app.ds-sidebar-open .ds-app-sidebar {
    transform: translateX(0);
    visibility: visible;
    transition-delay: 0s;
  }
  body.ds-layout-shell--app .ds-layout-shell__body {
    padding-inline: 0;
  }
  /* Only neutralize the desktop sidebar offset — the minimal footer keeps
       its own gutter, otherwise its link row touches the viewport edge. */
  body.ds-layout-shell--app .ds-site-footer {
    padding-inline: var(--ds-gutter);
  }
  body.ds-layout-shell--app .ds-app-sidebar__collapse {
    display: none;
  }
  body.ds-layout-shell--app .ds-app-sidebar__mobile-marketing {
    display: flex;
    flex-direction: column;
    gap: var(--ds-space-0-5);
  }

  .ds-app-sidebar__backdrop {
    position: fixed;
    top: var(--ds-app-header-h);
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--ds-backdrop);
    backdrop-filter: blur(2px);
    z-index: 89;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ds-dur-base) var(--ds-ease-out);
  }
  body.ds-layout-shell--app.ds-sidebar-open .ds-app-sidebar__backdrop {
    opacity: 1;
    pointer-events: auto;
  }
}

/* === Auth layout ===
   Sign-in, signup, password reset, e-mail confirmation and unsubscribe sit
   on the tinted light mesh. A single card carries a short brand-gradient
   bar on top. Login and signup pair the form with a dark brand panel
   (.ds-auth-aside): side by side in one framed split card from 992px,
   stacked below the form on smaller screens. */
.ds-layout-shell--auth .ds-layout-shell__body {
  align-items: center;
  justify-content: center;
  padding: clamp(2rem, 6vw, 4.5rem) var(--ds-space-4);
  min-height: calc(100vh - 120px);
  background: var(--ds-gradient-mesh-light);
}

.ds-layout-shell--auth .ds-auth-card {
  position: relative;
  width: 100%;
  max-width: 440px;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-lg);
  padding: var(--ds-space-10) var(--ds-space-6) var(--ds-space-8);
  overflow: hidden;
}

.ds-layout-shell--auth .ds-auth-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: var(--ds-gradient-brand);
}

@media (min-width: 576px) {
  .ds-layout-shell--auth .ds-auth-card {
    padding: var(--ds-space-12) var(--ds-space-10) var(--ds-space-10);
  }
}

.ds-layout-shell--auth .ds-auth-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  width: 100%;
  max-width: 480px;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-xl);
  box-shadow: var(--ds-shadow-lg);
  overflow: hidden;
}

.ds-layout-shell--auth .ds-auth-layout > .ds-auth-card {
  max-width: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.ds-layout-shell--auth .ds-auth-aside {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-5);
  padding: var(--ds-space-10) var(--ds-space-6);
  background: var(--ds-gradient-mesh-dark);
  color: var(--ds-ink-on-dark-body);
}

.ds-layout-shell--auth .ds-auth-aside::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--ds-pattern-grid-dark);
  background-size: 40px 40px;
  -webkit-mask-image: radial-gradient(
    ellipse 80% 70% at 70% 20%,
    var(--ds-black) 10%,
    transparent 75%
  );
  mask-image: radial-gradient(
    ellipse 80% 70% at 70% 20%,
    var(--ds-black) 10%,
    transparent 75%
  );
  pointer-events: none;
}

.ds-layout-shell--auth .ds-auth-aside > .ds-eyebrow {
  align-self: flex-start;
}

@media (min-width: 992px) {
  .ds-layout-shell--auth .ds-auth-layout {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    max-width: 1040px;
  }
  .ds-layout-shell--auth .ds-auth-aside {
    justify-content: center;
    padding: var(--ds-space-16) var(--ds-space-12);
  }
}

.ds-layout-shell--marketing .ds-layout-shell__body {
  padding: 0;
}

/* === Eyebrow ===
   The pill above a marketing heading: uppercase Jakarta, a gradient dot,
   a faint blue fill and hairline. Text --ds-blue-dark on the composited
   fill 7.73:1. On the dark stage it turns into a glass pill with the
   on-dark accent (above 6.8:1 on every glow). App pages carry no eyebrow.
   The two legacy dot elements stay hidden wherever an old template still
   renders them; the dot is drawn by ::before. */

.ds-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  max-width: 100%;
  padding: var(--ds-space-1-5) var(--ds-space-3) var(--ds-space-1-5)
    var(--ds-space-2-5);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-fw-bold);
  line-height: 1.2;
  letter-spacing: var(--ds-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ds-blue-dark);
  background: rgba(var(--ds-blue-rgb), 0.07);
  border: 1px solid rgba(var(--ds-blue-rgb), 0.16);
  border-radius: var(--ds-radius-full);
  box-shadow: var(--ds-shadow-pill);
  margin: 0;
}

.ds-eyebrow::before {
  content: "";
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-cta);
  box-shadow: 0 0 0 3px rgba(var(--ds-purple-rgb), 0.14);
}

.ds-eyebrow--on-dark,
.ds-section--dark .ds-eyebrow,
.ds-home-hero .ds-eyebrow,
.ds-page-hero .ds-eyebrow,
.ds-cta-band .ds-eyebrow,
.ds-auth-aside .ds-eyebrow {
  color: var(--ds-ink-on-dark-accent);
  background: var(--ds-surface-glass);
  border-color: var(--ds-border-glass);
  box-shadow: var(--ds-shadow-pill-on-dark);
}

.ds-eyebrow--on-dark::before,
.ds-section--dark .ds-eyebrow::before,
.ds-home-hero .ds-eyebrow::before,
.ds-page-hero .ds-eyebrow::before,
.ds-cta-band .ds-eyebrow::before,
.ds-auth-aside .ds-eyebrow::before {
  background: var(--ds-success-bright);
  box-shadow: 0 0 0 3px rgba(var(--ds-success-bright-rgb), 0.2);
}

.ds-eyebrow-dot,
.ds-app-eyebrow-dot {
  display: none;
}

/* === Gradient Text ===
   One emphasised phrase per headline in the brand gradient. On light
   surfaces every stop clears 4.5:1 (blue 6.67, violet 6.37, deep red
   5.37:1 on white); on the dark stage the pastel set clears 6.3:1 even on
   a glow. Forced-colors mode falls back to plain system text. */

.ds-gradient-text {
  background: var(--ds-gradient-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.ds-gradient-text--on-dark,
.ds-section--dark .ds-gradient-text,
.ds-home-hero .ds-gradient-text,
.ds-page-hero .ds-gradient-text,
.ds-cta-band .ds-gradient-text {
  background-image: var(--ds-gradient-text-on-dark);
}

/* When SplitType wraps the phrase in per-character spans for a reveal,
   motion.js gives every span its slice of the word's gradient; each span
   then has to clip that background to its own glyph. */
.ds-gradient-text .char {
  background-image: var(--ds-gradient-text);
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

@media (forced-colors: active) {
  .ds-gradient-text {
    background: none;
    color: CanvasText;
    -webkit-text-fill-color: currentColor;
  }
}

.ds-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  padding: 2px var(--ds-space-2-5);
  font-size: 0.75rem;
  font-weight: var(--ds-fw-semi);
  letter-spacing: 0;
  text-transform: none;
  border-radius: var(--ds-radius-full);
  line-height: 1.5;
  white-space: nowrap;
}

/* Solid badge backgrounds (not semi-transparent) for predictable WCAG AA
   contrast regardless of parent surface. At 12px + 600 weight every pair
   clears 6.5:1 on its own background. */
.ds-badge--neutral {
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-neutral-tint-text);
}
.ds-badge--success {
  background: var(--ds-success-tint-bg);
  color: var(--ds-success-tint-text);
}
.ds-badge--warn {
  background: var(--ds-warn-tint-bg);
  color: var(--ds-warn-tint-text);
}
.ds-badge--danger {
  background: var(--ds-danger-tint-bg);
  color: var(--ds-danger-tint-text);
}
.ds-badge--info {
  background: var(--ds-info-tint-bg);
  color: var(--ds-info-tint-text);
}
.ds-badge--violet {
  background: var(--ds-violet-tint-bg);
  color: var(--ds-violet-tint-text);
}
/* The recommended plan ("Empfohlen"): white on the CTA gradient, 6.37:1 at
   its violet end. */
.ds-badge--gradient {
  background: var(--ds-gradient-cta);
  color: var(--ds-white);
  font-weight: var(--ds-fw-bold);
  box-shadow: var(--ds-shadow-icon-tile);
}
/* Accent highlight ("Kein Abo nötig"): the logo orange as a readable pair. */
.ds-badge--accent {
  background: var(--ds-accent-tint-bg);
  color: var(--ds-accent-text);
}

/* Step counter — fixed round 24px square for numbered onboarding steps;
   the inline flow of the surrounding heading provides the gap. */
.ds-badge--step {
  width: 24px;
  height: 24px;
  padding: 0;
  justify-content: center;
  border-radius: var(--ds-radius-full);
}

/* === Alerts (info / success / warn / danger) ===
   Inline contextual banners for dashboards, auth-flow and form responses.
   Replaces Bootstrap .alert .alert-* — uses ds-tokens, radius-md, and the
   status-tint backgrounds from :root for consistent visual language. */

.ds-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-3);
  padding: var(--ds-space-3) var(--ds-space-4);
  border-radius: var(--ds-radius-md);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  border: 1px solid transparent;
  color: var(--ds-ink);
}

.ds-alert > i.bi,
.ds-alert > i[class*="bi-"] {
  flex-shrink: 0;
  font-size: var(--ds-text-lg);
  line-height: 1.2;
  margin-top: 1px;
}

.ds-alert strong {
  color: inherit;
}
.ds-alert--success {
  background: var(--ds-success-tint);
  border-color: rgba(var(--ds-success-rgb), 0.18);
  color: var(--ds-success);
}
.ds-alert--warn {
  background: var(--ds-warn-tint);
  border-color: rgba(var(--ds-warn-rgb), 0.18);
  /* --ds-warn measures 4.39:1 on the warn tint and misses the 4.5:1 AA floor
     for body text; --ds-warn-deep reaches 7.92:1. The sibling variants
     (info/success/danger) already clear AA on their own tints, so only this
     one changes. */
  color: var(--ds-warn-deep);
}
.ds-alert--danger {
  background: var(--ds-danger-tint);
  border-color: rgba(var(--ds-danger-rgb), 0.18);
  color: var(--ds-danger);
}

/* Alert body text inherits ink-body, only icon + title carry the status-color. */
.ds-alert--info .ds-alert__body,
.ds-alert--success .ds-alert__body,
.ds-alert--warn .ds-alert__body,
.ds-alert--danger .ds-alert__body {
  color: var(--ds-ink);
}

/* === Buttons ===
   Jakarta labels at 700. The primary carries the CTA gradient (blue to
   violet; white text 6.67:1 and 6.37:1 at its two ends), a soft inner
   highlight and a blue glow; on hover the gradient slides toward violet,
   the glow deepens and the button rises 1px. Secondary is white with a
   hairline, ghost is text-only, and both have on-dark variants for the
   dark stage. Every variant shares the 44px control height (WCAG 2.5.8);
   --sm is the compact 36px step for dense tables, --lg the 52px hero size. */

.ds-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  min-height: var(--ds-control-h);
  padding: 0 var(--ds-space-5);
  font-family: var(--ds-font-display);
  font-size: 0.9375rem;
  font-weight: var(--ds-fw-bold);
  line-height: 1.25;
  letter-spacing: -0.005em;
  text-align: center;
  border: 1px solid transparent;
  border-radius: var(--ds-radius-sm);
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--ds-dur-fast) var(--ds-ease-standard),
    background-position var(--ds-dur-base) var(--ds-ease-out),
    border-color var(--ds-dur-fast) var(--ds-ease-standard),
    color var(--ds-dur-fast) var(--ds-ease-standard),
    box-shadow var(--ds-dur-base) var(--ds-ease-standard),
    transform var(--ds-dur-base) var(--ds-ease-out);
  position: relative;
  white-space: nowrap;
  /* A button can never be wider than the column it sits in. Without this a
     long label plus nowrap simply pushes past the viewport, and the page-level
     overflow-x:clip hides the fact instead of scrolling to it. */
  max-width: 100%;
}

.ds-button:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* Filled buttons carry the ring colour as their own background, so they
   need the inset white band to keep the focus indicator distinguishable. */
.ds-button--primary:focus-visible {
  box-shadow: var(--ds-shadow-focus-on-brand);
}

/* On the dark stage the blue ring measures 2.87:1, so every button there
   takes the on-dark ring (9.82:1). */
.ds-section--dark .ds-button:focus-visible,
.ds-home-hero .ds-button:focus-visible,
.ds-page-hero .ds-button:focus-visible,
.ds-cta-band .ds-button:focus-visible,
.ds-site-footer .ds-button:focus-visible,
.ds-sticky-cta .ds-button:focus-visible,
.ds-button--secondary--on-dark:focus-visible {
  box-shadow: var(--ds-shadow-focus-on-dark);
}

.ds-button:disabled,
.ds-button[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Press state: hover is a pointer-only signal, so a touch tap needs its own
   confirmation that it registered. The button settles back down. */
.ds-button:not(:disabled):not([aria-disabled="true"]):active {
  transform: translateY(1px);
}

/* Only the forward arrow moves on hover: it tells where the button leads. */
.ds-button i,
.ds-button svg {
  flex-shrink: 0;
  transition: transform var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-button:hover i.bi-arrow-right {
  transform: translateX(3px);
}

.ds-button--primary {
  color: var(--ds-white);
  background-color: var(--ds-blue);
  background-image: var(--ds-gradient-cta);
  background-size: 180% 100%;
  background-position: 0% 50%;
  box-shadow: var(--ds-shadow-primary);
}

/* The explicit color + a.<class>:hover selector defeats the global
   a:hover { color: var(--ds-blue-dark) } base rule, which would otherwise
   recolour the label to dark blue on the gradient. */
.ds-button--primary:hover,
a.ds-button--primary:hover {
  color: var(--ds-white);
  background-position: 100% 50%;
  box-shadow: var(--ds-shadow-primary-hover);
  transform: translateY(-1px);
}

.ds-button--primary:not(:disabled):active {
  background-image: var(--ds-gradient-cta-active);
}

.ds-button--secondary {
  background: var(--ds-surface);
  color: var(--ds-ink);
  border-color: var(--ds-border-strong);
  box-shadow: var(--ds-shadow-sm);
}

.ds-button--secondary:hover,
a.ds-button--secondary:hover {
  background: var(--ds-surface);
  color: var(--ds-blue-dark);
  border-color: rgba(var(--ds-blue-rgb), 0.45);
  box-shadow: var(--ds-shadow-md);
}

/* On the dark stage: a glass button with a light hairline. Also applied
   automatically to a plain --secondary inside a dark section. */
.ds-button--secondary--on-dark,
.ds-section--dark .ds-button--secondary,
.ds-home-hero .ds-button--secondary,
.ds-page-hero .ds-button--secondary,
.ds-cta-band .ds-button--secondary {
  background: var(--ds-surface-glass);
  color: var(--ds-ink-on-dark-strong);
  border-color: rgba(var(--ds-white-rgb), 0.22);
  box-shadow: var(--ds-shadow-pill-on-dark);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.ds-button--secondary--on-dark:hover,
a.ds-button--secondary--on-dark:hover,
.ds-section--dark .ds-button--secondary:hover,
.ds-home-hero .ds-button--secondary:hover,
.ds-page-hero .ds-button--secondary:hover,
.ds-cta-band .ds-button--secondary:hover {
  background: rgba(var(--ds-white-rgb), 0.12);
  color: var(--ds-white);
  border-color: rgba(var(--ds-white-rgb), 0.4);
}

.ds-button--ghost {
  background: transparent;
  color: var(--ds-blue);
}

.ds-button--ghost:hover,
a.ds-button--ghost:hover {
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-blue-dark);
}

.ds-button--ghost--on-dark {
  color: var(--ds-ink-on-dark-strong);
}

.ds-button--ghost--on-dark:hover,
a.ds-button--ghost--on-dark:hover {
  background: var(--ds-surface-glass-strong);
  color: var(--ds-white);
}

.ds-button--danger {
  background: var(--ds-danger);
  color: var(--ds-white);
}

.ds-button--danger:hover,
a.ds-button--danger:hover {
  background: var(--ds-danger-bright);
  color: var(--ds-white);
  box-shadow: var(--ds-shadow-danger-hover);
}

.ds-button--danger:focus-visible {
  box-shadow: var(--ds-shadow-focus-on-danger);
}

.ds-button--sm {
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-3-5);
  font-size: var(--ds-text-body-sm);
  border-radius: var(--ds-radius-xs);
}

.ds-button--lg {
  min-height: var(--ds-control-h-lg);
  padding: 0 var(--ds-space-6);
  font-size: var(--ds-text-body);
  border-radius: var(--ds-radius-md);
}

/* A full-width button may wrap: a long label (the sample-card CTA, the app
   plan CTA with its price) breaks onto a second line instead of being cut. */
.ds-button--block {
  display: flex;
  width: 100%;
  white-space: normal;
  padding-block: var(--ds-space-2);
}

/* On a phone a sentence-length label has no room to stay on one line.
   Allowing the wrap costs nothing for short labels and is the only
   alternative to cutting the label off at the screen edge. */
@media (max-width: 575px) {
  .ds-button {
    white-space: normal;
    padding-block: var(--ds-space-2);
  }
}

/* Loading state — applied while a submit/POST is in flight. Pairs with an
   inline <span class="ds-spinner ds-spinner--sm"> before the label; pointer
   events are disabled so the styled state cannot fire a double submit. */
.ds-button.is-loading {
  pointer-events: none;
  opacity: 0.8;
}

/* === Cards ===
   White, a blue-tinted hairline, 14px radius and a soft shadow. Only a card
   that is itself a link lifts on hover; a static card that moved promised a
   click it could not keep. --elevated floats a little higher, --glass is
   the translucent panel for the dark stage, --featured the recommended
   plan with a gradient frame and halo. */
.ds-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  padding: var(--ds-space-6);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-sm);
  transition: var(--ds-card-transition);
  overflow: hidden;
  color: var(--ds-ink);
  text-decoration: none;
}

@media (max-width: 575px) {
  .ds-card {
    padding: var(--ds-space-5);
  }
}

a.ds-card:hover,
a.ds-card:focus-visible {
  transform: translateY(var(--ds-card-lift));
  border-color: rgba(var(--ds-blue-rgb), 0.3);
  box-shadow: var(--ds-shadow-lg);
  color: var(--ds-ink);
}

.ds-card--elevated {
  border-color: var(--ds-border-subtle);
  box-shadow: var(--ds-shadow-md);
}

.ds-card--glass {
  background: var(--ds-surface-glass);
  border-color: var(--ds-border-glass);
  box-shadow: var(--ds-shadow-pill-on-dark);
  color: var(--ds-ink-on-dark-body);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.ds-card--compact {
  padding: var(--ds-space-4);
  gap: var(--ds-space-2);
}

/* Recommended ("Empfohlen") tier: a 2px brand-gradient frame drawn by a
   padding-box/border-box background pair, plus a violet halo. No scale or
   translate, so the grid stays aligned. */
.ds-card--featured {
  border: 2px solid transparent;
  background:
    linear-gradient(var(--ds-surface), var(--ds-surface)) padding-box,
    var(--ds-gradient-brand) border-box;
  box-shadow: var(--ds-shadow-glow-featured);
}
/* === Card Variants === */

.ds-card--stat {
  align-items: flex-start;
}

.ds-card--stat .ds-card__icon {
  /* The shared gradient tile; only the stat-card size stays here. */
  width: 36px;
  height: 36px;
  border-radius: var(--ds-radius-sm);
  font-size: var(--ds-text-lg);
}

.ds-card--stat .ds-card__label {
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--ds-tracking-meta);
  color: var(--ds-ink-mute);
  margin: 0;
}

.ds-card--stat .ds-card__value {
  font-family: var(--ds-font-display);
  font-variant-numeric: tabular-nums;
  font-size: 28px;
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
  margin: 0;
  line-height: var(--ds-lh-tight);
}

.ds-card--stat .ds-card__context {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-soft);
  margin: 0;
}

.ds-card--stat .ds-card__action {
  margin-top: auto;
  padding-top: var(--ds-space-4);
}

.ds-card--feature {
  align-items: flex-start;
  gap: var(--ds-space-3);
}

.ds-card__title {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0;
  line-height: 1.3;
  letter-spacing: var(--ds-tracking-tight);
}

.ds-card--feature .ds-card__title {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0;
  line-height: 1.3;
}

.ds-card--feature .ds-card__body {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
  line-height: var(--ds-lh-prose);
  margin: 0;
}

.ds-card--pricing {
  padding: var(--ds-space-6);
  gap: var(--ds-space-4);
}

.ds-card--pricing .ds-button {
  margin-top: auto;
}

/* Top-up package grid — auto-fit distributes all five packages evenly
   (5-up on desktop) instead of the orphaning fixed 4-column row. */
.ds-topup-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--ds-space-4);
}

@media (max-width: 575px) {
  .ds-topup-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* An odd package count would orphan the last card half-width. */
  .ds-topup-grid > .ds-card:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* Two cards per row leave the buy button barely 80px of label width inside
     a 24px-padded card, and a .ds-button label can neither wrap nor
     ellipsize. The tighter inline padding buys the ~24px that keep the label
     intact down to 320px viewports. */
  .ds-topup-grid > .ds-card--pricing {
    padding-inline: var(--ds-space-3);
  }
}

/* Active-plan variant — brand-blue border ring to signal the subscriber's
   current tier. Green is not part of the plan-card palette (the brand ramp is
   blue/purple/red), so the active ring uses the primary brand blue. */
.ds-card--pricing.ds-card--current {
  border-color: var(--ds-blue);
  box-shadow: 0 0 0 4px rgba(var(--ds-blue-rgb), 0.12);
}

.ds-card--content {
  gap: 0;
}

.ds-card--content .ds-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-3);
  padding-bottom: var(--ds-space-3);
  border-bottom: 1px solid var(--ds-border-subtle);
  margin-bottom: var(--ds-space-4);
}

.ds-card--content .ds-card__header h2,
.ds-card--content .ds-card__header h3 {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0;
  letter-spacing: var(--ds-tracking-tight);
}

.ds-card--content .ds-card__body {
  flex: 1;
}

.ds-card--lead {
  flex-direction: column;
  align-items: stretch;
  gap: var(--ds-space-3);
  padding: var(--ds-space-6) var(--ds-space-5);
  height: 100%;
}

.ds-card--lead .ds-card__title {
  font-size: var(--ds-text-body);
  line-height: 1.3;
  min-height: 2.6em;
}

.ds-card--lead .ds-button {
  margin-top: auto;
}

.ds-card--lead.ds-card--lead-blurred .ds-lead-contact {
  filter: blur(4px);
  user-select: none;
  pointer-events: none;
}

.ds-card__eyebrow {
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-fw-bold);
  letter-spacing: var(--ds-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ds-blue);
  margin: 0;
}

.ds-card__title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-3);
}

.ds-card__title-row .ds-card__title {
  margin: 0;
}

.ds-card__lock {
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-lg);
  flex-shrink: 0;
}

.ds-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
}

.ds-card__meta-item {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
}

.ds-card__meta-item .bi {
  color: var(--ds-blue);
  font-size: var(--ds-text-body-sm);
}

.ds-card__score {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 4px var(--ds-space-3);
  padding: var(--ds-space-3) var(--ds-space-4);
  background: var(--ds-surface-tint);
  border-radius: var(--ds-radius-sm);
}

.ds-card__score-label {
  grid-column: 1 / 2;
  grid-row: 1 / 2;
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-mute);
}

.ds-card__score-value {
  grid-column: 2 / 3;
  grid-row: 1 / 3;
  align-self: center;
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  font-variant-numeric: tabular-nums;
}

.ds-card__score-bar {
  grid-column: 1 / 2;
  grid-row: 2 / 3;
  height: 6px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-3);
  overflow: hidden;
}

.ds-card__score-bar-fill {
  height: 100%;
  background: var(--ds-gradient-meter);
  border-radius: var(--ds-radius-full);
}

.ds-card__highlight {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-2);
  padding: var(--ds-space-3) var(--ds-space-4);
  background: var(--ds-surface-tint);
  border-radius: var(--ds-radius-sm);
  border-left: 3px solid var(--ds-blue);
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-prose);
  margin: 0;
  min-height: 5.5em;
}

.ds-card__highlight .bi {
  color: var(--ds-blue);
  font-size: var(--ds-text-body);
  flex-shrink: 0;
  margin-top: var(--ds-space-0-5);
}

.ds-card__locked-fields {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  padding: var(--ds-space-3) 0 0;
  border-top: 1px dashed var(--ds-border-subtle);
}

.ds-card__locked-fields > span {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  padding: var(--ds-space-1) var(--ds-space-2-5);
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-muted);
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
}

.ds-card__locked-fields .bi {
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-eyebrow);
}

.ds-sample-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--ds-space-5);
}

@media (max-width: 767px) {
  .ds-sample-grid {
    grid-template-columns: 1fr;
    gap: var(--ds-space-4);
  }
}

.ds-text-muted {
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-body-sm);
}

.ds-hero {
  position: relative;
  padding: clamp(48px, 8vw, 96px) var(--ds-space-6);
  overflow: hidden;
  isolation: isolate;
}

.ds-hero__content,
.ds-hero__copy {
  position: relative;
  z-index: 1;
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-4);
}

.ds-hero__title {
  font-size: var(--ds-text-hero-standard);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
  margin: 0;
  text-wrap: balance;
  /* This heading renders third-party data at up to 48px: the scraped
     company name on the unlocked-lead detail page and the user's own
     e-mail on the dashboard. German compounds and address-style e-mails
     offer no break opportunity, and the page-level `overflow-x: clip`
     would then silently cut the name off at the viewport edge rather than
     scroll. Same reasoning as the lead-table card view, which already
     carries this rule at a third of the size. */
  overflow-wrap: anywhere;
}

.ds-hero__lead {
  font-size: var(--ds-text-body);
  line-height: var(--ds-lh-prose);
  color: var(--ds-ink-soft);
  max-width: 640px;
  margin: 0;
}

.ds-hero__content > .ds-eyebrow,
.ds-hero__copy > .ds-eyebrow {
  align-self: flex-start;
}

.ds-hero__actions {
  display: flex;
  gap: var(--ds-space-3);
  flex-wrap: wrap;
  margin-top: var(--ds-space-2);
}

.ds-hero--standard {
  background: var(--ds-white);
  padding-top: var(--ds-space-10);
  padding-bottom: var(--ds-space-10);
}

/* The welcome page's greeting on the dark stage (with .ds-section--dark):
   a rounded panel above the onboarding steps. */
.ds-hero.ds-hero--stage {
  margin-bottom: var(--ds-space-6);
  padding: clamp(2.5rem, 6vw, 4rem) clamp(1.5rem, 5vw, 3.5rem);
  border-radius: var(--ds-radius-xl);
}

.ds-section--dark .ds-hero__title {
  color: var(--ds-ink-on-dark-strong);
}

.ds-section--dark .ds-hero__lead {
  color: var(--ds-ink-on-dark-body);
}

/* The marketing standard-hero sits on a plain white surface; its atmosphere
   comes from the surrounding section, not from the hero itself. */
.ds-layout-shell--marketing .ds-hero--standard {
  background: var(--ds-white);
}

/* === Sections ===
   Three surfaces alternate down a marketing page: --light (white), --tint
   (the tint under two faint brand glows) and --dark (the navy-to-violet
   mesh with a fine grid that fades out toward the edges). */

.ds-section {
  position: relative;
  width: 100%;
  /* Vertical rhythm only: the one page gutter comes from .ds-wrap or
     .ds-section__inner, so a phone keeps a 358px column at 390px. */
  padding: var(--ds-section-y) 0;
  /* clip, not hidden: a hidden overflow would make every section a scroll
     container and pin the sticky table of contents to the section top. */
  overflow: clip;
}

.ds-section__inner {
  max-width: 1280px;
  margin: 0 auto;
  padding-inline: var(--ds-gutter);
  position: relative;
  z-index: 1;
}

.ds-section__title {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-section);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
  text-align: center;
  margin: 0 0 var(--ds-space-4);
  text-wrap: balance;
}

.ds-section__lead {
  font-size: var(--ds-text-lead);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-soft);
  text-align: center;
  max-width: 720px;
  margin: 0 auto var(--ds-space-8);
}

.ds-section--light {
  background: var(--ds-surface);
}
.ds-section--tint,
.ds-section--atmo {
  background: var(--ds-gradient-mesh-light);
}

.ds-section--dark {
  background: var(--ds-gradient-mesh-dark);
  color: var(--ds-ink-on-dark-body);
  isolation: isolate;
}

.ds-section--dark .ds-section__title {
  color: var(--ds-ink-on-dark-strong);
}

.ds-section--dark .ds-section__lead {
  color: var(--ds-ink-on-dark-body);
}

/* The fine grid of the dark stage, faded out by a radial mask so it reads
   as texture behind the centre, never as a frame. Shared by every dark
   surface. */
.ds-section--dark::before,
.ds-home-hero::before,
.ds-page-hero::before,
.ds-cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--ds-pattern-grid-dark);
  background-size: 48px 48px;
  background-position: center top;
  -webkit-mask-image: radial-gradient(
    ellipse 70% 60% at 50% 35%,
    var(--ds-black) 20%,
    transparent 75%
  );
  mask-image: radial-gradient(
    ellipse 70% 60% at 50% 35%,
    var(--ds-black) 20%,
    transparent 75%
  );
  pointer-events: none;
}

/* The section after a page hero continues the page; it does not open a new
   chapter, so it takes half the usual top space. */
.ds-page-hero + .ds-section {
  padding-top: clamp(2.5rem, 5vw, 4rem);
}

/* === Page Hero ===
   The compact dark band that opens every marketing subpage (blog, contact,
   legal pages): the dark mesh and grid of the home hero, a breadcrumb, an
   eyebrow, the H1 at --ds-text-hero-standard and one lead line. --center
   centres the stack for short pages. The lead is a hero intro line, a
   layout width rather than running text. */
.ds-page-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ds-gradient-mesh-dark);
  color: var(--ds-ink-on-dark-body);
  padding: clamp(2.75rem, 6vw, 5rem) 0 clamp(2.75rem, 5.5vw, 4.5rem);
}

.ds-page-hero__inner {
  position: relative;
  z-index: 1;
  max-width: 1280px;
  margin: 0 auto;
  padding-inline: var(--ds-gutter);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-4);
}

.ds-page-hero--center .ds-page-hero__inner {
  align-items: center;
  text-align: center;
}

.ds-page-hero__title {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-hero-standard);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-display);
  color: var(--ds-ink-on-dark-strong);
  margin: 0;
  text-wrap: balance;
  overflow-wrap: anywhere;
}

.ds-page-hero__lead {
  max-width: 46rem;
  margin: 0;
  font-size: var(--ds-text-lead);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-on-dark-body);
}

.ds-page-hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2) var(--ds-space-5);
  margin: var(--ds-space-1) 0 0;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-on-dark-muted);
}

.ds-page-hero .ds-breadcrumb {
  margin-bottom: var(--ds-space-1);
  color: var(--ds-ink-on-dark-muted);
}

.ds-page-hero .ds-breadcrumb a {
  color: var(--ds-ink-on-dark-accent);
}

.ds-page-hero .ds-breadcrumb a:hover {
  color: var(--ds-ink-on-dark-strong);
}

/* Article meta (date, reading time, pillar badge) inside the page hero. */
.ds-page-hero__meta .article-metadata-item > i {
  color: var(--ds-ink-on-dark-muted);
}

.ds-page-hero .article-metadata-badge {
  background: var(--ds-surface-glass-strong);
  border: 1px solid var(--ds-border-glass);
  color: var(--ds-ink-on-dark-accent);
}

/* Article cards: one column on a phone, two from 640px, three from 992px. */
.ds-blog-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-6);
}

@media (min-width: 640px) {
  .ds-blog-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 992px) {
  .ds-blog-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* The contact form card: roomier than a content card. */
.ds-card.ds-contact-form {
  padding: clamp(1.5rem, 3vw, 2.5rem);
}

/* === CTA-Band ===
   The closing call to action on the dark stage: mesh, grid, an eyebrow, a
   large headline with one gradient phrase, one line of facts and the two
   actions, all centred. Also renders inside the full footer. */

.ds-cta-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(4rem, 9vw, 7rem) var(--ds-gutter);
  background: var(--ds-gradient-mesh-dark);
  color: var(--ds-ink-on-dark-body);
}

.ds-cta-band__content {
  position: relative;
  max-width: 880px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ds-space-8);
  text-align: center;
}

.ds-cta-band__content > div:first-child {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ds-space-4);
}

.ds-cta-band__title {
  font-family: var(--ds-font-display);
  font-size: clamp(2rem, 1.35rem + 2.7vw, 3.25rem);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-display);
  color: var(--ds-ink-on-dark-strong);
  margin: 0;
  text-wrap: balance;
}

.ds-cta-band__lead {
  font-size: var(--ds-text-lead);
  color: var(--ds-ink-on-dark-body);
  margin: 0;
  line-height: var(--ds-lh-base);
}

.ds-cta-band__actions {
  display: flex;
  gap: var(--ds-space-3);
  flex-wrap: wrap;
  justify-content: center;
}

@media (max-width: 575px) {
  .ds-cta-band__actions {
    flex-direction: column;
    align-self: stretch;
  }
}

/* === Form Controls === */

.ds-form-field {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
  margin-bottom: var(--ds-space-4);
}

.ds-form-label {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-0-5);
}

/* 16px text stops iOS from zooming into a focused field; the border clears
   the 3:1 non-text floor so the field is findable without its label. */
.ds-form-control {
  display: block;
  width: 100%;
  min-height: var(--ds-control-h);
  padding: var(--ds-space-2-5) var(--ds-space-3);
  font-family: inherit;
  font-size: var(--ds-text-body);
  line-height: 1.4;
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-control);
  border-radius: var(--ds-radius-sm);
  transition:
    border-color var(--ds-dur-fast) var(--ds-ease-out),
    box-shadow var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-form-control:focus-visible,
.ds-form-control:focus {
  outline: none;
  border-color: var(--ds-blue);
  box-shadow: var(--ds-shadow-focus);
}

.ds-form-control:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  background: var(--ds-surface-tint);
}

/* Placeholders are text and fall under the 4.5:1 floor like any other. With
   no rule at all the browser default applied, which differs per engine and
   lands close to the limit in Chrome. --ds-ink-mute is the same muted ink
   the crispy bridge already uses, so both paths now read identically. */
.ds-form-control::placeholder {
  color: var(--ds-ink-mute);
  opacity: 1;
}

.ds-form-control--error {
  border-color: var(--ds-danger);
  background: var(--ds-danger-tint);
  box-shadow: var(--ds-shadow-field-danger);
}

.ds-form-control--error:focus-visible,
.ds-form-control--error:focus {
  border-color: var(--ds-danger);
  box-shadow: var(--ds-shadow-focus-danger);
}

.ds-form-control--success {
  border-color: var(--ds-success);
  background: var(--ds-success-tint);
}

.ds-form-control--success:focus-visible,
.ds-form-control--success:focus {
  border-color: var(--ds-success);
  box-shadow: var(--ds-shadow-focus-success);
}

.ds-form-help {
  margin: var(--ds-space-1) 0 0;
  font-size: var(--ds-text-caption);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
}

.ds-form-help--error {
  color: var(--ds-danger);
  font-weight: var(--ds-fw-medium);
}

textarea.ds-form-control {
  resize: vertical;
  min-height: 96px;
}

/* Password field with a show/hide toggle. static/js/password-toggle.js
   wraps every input carrying data-ls-password-toggle in .ds-password-field
   and appends the .ds-password-toggle button inside the field's right
   edge; the input reserves the room so typed text never runs under it. */
.ds-password-field {
  position: relative;
  display: block;
}

.ds-password-field > input {
  padding-right: 6.5rem;
}

.ds-password-toggle {
  position: absolute;
  top: 50%;
  right: var(--ds-space-1);
  transform: translateY(-50%);
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-3);
  border: 0;
  border-radius: var(--ds-radius-xs);
  background: transparent;
  color: var(--ds-blue);
  font: inherit;
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  cursor: pointer;
}

.ds-password-toggle:hover {
  background: var(--ds-neutral-tint-bg);
}

.ds-password-toggle:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* ============================================================================
 * ds-chip-input — progressive-enhancement multi-value picker (wishes form)
 * JS (wishes_form.js) hides the backing textarea and renders selected values
 * as removable chips above a single-line entry input.
 * ==========================================================================*/
.ds-chip-input__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  margin-bottom: var(--ds-space-2);
}

.ds-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-1) var(--ds-space-3);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-tint);
  color: var(--ds-ink);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
}

.ds-chip__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: none;
  border-radius: var(--ds-radius-full);
  background: transparent;
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-body);
  line-height: 1;
  cursor: pointer;
}

.ds-chip__remove:hover {
  color: var(--ds-danger);
}

.ds-chip__remove:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* Grow the remove button's tap target to the 44px touch floor below 768px
 * without enlarging the visible glyph: a transparent, centered overlay
 * extends the clickable area while the chip layout stays compact. */
@media (max-width: 767px) {
  .ds-chip__remove {
    position: relative;
  }

  .ds-chip__remove::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }
}

/* ============================================================================
 * ds-check — canonical checkbox / radio / switch family
 * Replaces raw Bootstrap .form-check markup. The wrapping <label> carries the
 * hit area, so no for/id pairing is required:
 *   <label class="ds-check">
 *       <input class="ds-check__input" type="checkbox">
 *       <span class="ds-check__label">Text</span>
 *   </label>
 * Switch variant: <label class="ds-check ds-check--switch"> with the same
 * children. Below 768px the row grows to the 44px touch floor while the
 * visible control stays 24px.
 * ========================================================================== */
.ds-check {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-3);
  cursor: pointer;
  margin: 0;
}

/* Label-less variant for table row selection — the aria-label on the
   input carries the accessible name, so no gap is needed. */
.ds-check--bare {
  display: inline-flex;
  gap: 0;
  vertical-align: middle;
}

.ds-check__input {
  appearance: none;
  -webkit-appearance: none;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  margin: 0;
  background-color: var(--ds-surface);
  border: 1px solid var(--ds-border-control);
  border-radius: var(--ds-radius-xs);
  cursor: pointer;
  transition:
    background-color var(--ds-dur-fast) var(--ds-ease-out),
    border-color var(--ds-dur-fast) var(--ds-ease-out),
    box-shadow var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-check__input:hover {
  border-color: var(--ds-blue);
}

.ds-check__input:focus-visible {
  outline: none;
  border-color: var(--ds-blue);
  box-shadow: var(--ds-shadow-focus);
}

/* The check glyph ships as an inline SVG data-URI (white stroke) — same
   asset pattern as the select chevron above; not a component color literal. */
.ds-check__input:checked {
  background-color: var(--ds-blue);
  border-color: var(--ds-blue);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 14px 14px;
}

.ds-check__input[type="radio"] {
  border-radius: var(--ds-radius-full);
}

/* Radios keep a surface-colored dot punched out of the filled circle. */
.ds-check__input[type="radio"]:checked {
  background-image: none;
  box-shadow: inset 0 0 0 6px var(--ds-blue);
  background-color: var(--ds-surface);
}

/* A checked box is filled with the very colour the focus ring is drawn in —
   without the inset white band the ring reads as a blue square inside a blue
   square. This is exactly the case --ds-shadow-focus-on-brand exists for. */
.ds-check__input:checked:focus-visible {
  box-shadow: var(--ds-shadow-focus-on-brand);
}

.ds-check__input[type="radio"]:checked:focus-visible {
  box-shadow:
    inset 0 0 0 6px var(--ds-blue),
    var(--ds-shadow-focus-on-brand);
}

.ds-check__input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background-color: var(--ds-surface-tint);
}

/* Checked + disabled keeps the blue fill (dimmed via opacity) so the white
   check glyph stays visible instead of washing out on the tint surface. */
.ds-check__input:checked:disabled {
  background-color: var(--ds-blue);
  border-color: var(--ds-blue);
}

.ds-check__input[type="radio"]:checked:disabled {
  background-color: var(--ds-surface);
}

.ds-check__input:disabled ~ .ds-check__label {
  opacity: 0.6;
  cursor: not-allowed;
}

.ds-check__label {
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink);
  /* Vertically centers the first label line against the 24px control. */
  padding-top: var(--ds-space-0-5);
}

/* The consent labels are the one place in the product where a link sits inside
   running body copy, and there it was marked by colour alone: --ds-blue against
   --ds-ink measures 1.92:1, well under the 3:1 that WCAG 1.4.1 requires before
   colour may carry the distinction by itself. Underlining is also what makes
   these two links aimable now that they no longer carry an oversized hit box. */
.ds-check__label a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Switch — pill track with a sliding knob, same input element underneath. */
.ds-check--switch .ds-check__input {
  position: relative;
  width: 44px;
  border-radius: var(--ds-radius-full);
  background-color: var(--ds-surface-muted);
  border-color: var(--ds-border);
}

.ds-check--switch .ds-check__input::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface);
  box-shadow: var(--ds-shadow-sm);
  border: 1px solid var(--ds-border-subtle);
  transition: transform var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-check--switch .ds-check__input:checked {
  background-color: var(--ds-blue);
  border-color: var(--ds-blue);
  background-image: none;
}

.ds-check--switch .ds-check__input:checked::before {
  transform: translateX(20px);
}

/* Same reasoning as the checked checkbox: the switch track carries the ring
   colour once it is on. */
.ds-check--switch .ds-check__input:checked:focus-visible {
  box-shadow: var(--ds-shadow-focus-on-brand);
}

/* Mobile touch floor: the full label row becomes the 44px hit area while
   the visible 24px control keeps the desktop proportions. */
@media (max-width: 767px) {
  .ds-check {
    align-items: center;
    min-height: 44px;
  }
  .ds-check__label {
    padding-top: 0;
  }
} /* === Panels === */

/* === Settings Section + Danger Zone === */

.ds-settings-section {
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-md);
  padding: var(--ds-space-5);
  margin-top: var(--ds-space-5);
  box-shadow: var(--ds-shadow-sm);
}

.ds-settings-section__header {
  padding-bottom: var(--ds-space-3);
  border-bottom: 1px solid var(--ds-border-subtle);
  margin-bottom: var(--ds-space-4);
}

.ds-settings-section__title {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
}

.ds-settings-section--danger {
  border-color: var(--ds-danger);
  background: var(--ds-surface);
}

.ds-settings-section--danger .ds-settings-section__title {
  color: var(--ds-danger);
}

.ds-danger-block {
  padding: var(--ds-space-4);
  border: 1px solid var(--ds-danger-tint);
  border-radius: var(--ds-radius-sm);
  background: var(--ds-surface);
  margin-top: var(--ds-space-3);
}

.ds-danger-block + .ds-danger-block {
  margin-top: var(--ds-space-4);
}

.ds-danger-block h3 {
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0 0 var(--ds-space-2);
}

.ds-danger-block p {
  color: var(--ds-ink-soft);
  margin: 0 0 var(--ds-space-3);
  line-height: var(--ds-lh-prose);
}

.ds-danger-block ul {
  margin: 0 0 var(--ds-space-3);
  padding-left: var(--ds-space-5);
  color: var(--ds-ink-soft);
}

/* === Empty State ===
   What is empty, why it matters, one next step: a centred block with an
   80px illustration, an 18px title, a muted line and one primary action.
   The 28rem body width is a component width, not a running-text cap. */

.ds-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--ds-space-12) var(--ds-space-6);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-sm);
  gap: var(--ds-space-3);
}

.ds-empty-state__illustration {
  width: 80px;
  height: 80px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--ds-radius-full);
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-blue);
  font-size: 32px;
  margin-bottom: var(--ds-space-1);
}

.ds-empty-state__illustration svg {
  width: 100%;
  height: 100%;
}

.ds-empty-state__title {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
  margin: 0;
}

.ds-empty-state__body {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
  max-width: 28rem;
  margin: 0;
  line-height: var(--ds-lh-base);
}

/* Success-tinted illustration tile used on the checkout-success page. */
.ds-empty-state__illustration--success {
  background: var(--ds-success-tint-bg);
  color: var(--ds-success);
}

/* Button-row inside an empty state. */
.ds-empty-state__actions {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-2);
} /* === Trust Strip === */

/* === Compare Table ===
   A bordered table with sentence-case headers and the LeadSuche column on
   a tint with a gradient bar over its header. No badge: the table states
   features, it does not award itself a title. Inside .ds-section--dark the
   table becomes a glass panel: on-dark ink, the LeadSuche column on a
   violet wash (strong ink 13.71:1, checks 8.70:1 on it). */

.ds-compare-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-lg);
  overflow: hidden;
}

.ds-compare-caption {
  caption-side: bottom;
  padding: var(--ds-space-4) var(--ds-space-1) 0;
  font-size: var(--ds-text-caption);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
  text-align: left;
}

.ds-compare-table th,
.ds-compare-table td {
  padding: var(--ds-space-4) var(--ds-space-5);
  text-align: left;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink);
  border-bottom: 1px solid var(--ds-border-subtle);
  vertical-align: middle;
}

.ds-compare-table thead th {
  background: var(--ds-surface-tint);
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink-mute);
  font-size: var(--ds-text-caption);
  border-bottom-color: var(--ds-border);
}

.ds-compare-table tbody th {
  font-weight: var(--ds-fw-semi);
}

.ds-compare-table tbody tr:last-child th,
.ds-compare-table tbody tr:last-child td {
  border-bottom: 0;
}

.ds-compare-table .ds-compare-table__col--highlight {
  position: relative;
  background: var(--ds-neutral-tint-bg);
  font-weight: var(--ds-fw-semi);
}

.ds-compare-table thead th.ds-compare-table__col--highlight {
  color: var(--ds-blue);
  font-size: var(--ds-text-body-sm);
}

.ds-compare-table thead th.ds-compare-table__col--highlight::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--ds-gradient-brand);
}

.ds-compare-wrap {
  position: relative;
}

/* ---- On the dark stage ---- */
.ds-section--dark .ds-compare-table {
  background: var(--ds-surface-glass);
  border-color: var(--ds-border-glass);
  color: var(--ds-ink-on-dark-body);
  box-shadow: var(--ds-shadow-xl);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.ds-section--dark .ds-compare-table th,
.ds-section--dark .ds-compare-table td {
  color: var(--ds-ink-on-dark-body);
  border-bottom-color: var(--ds-surface-dark-border);
}

.ds-section--dark .ds-compare-table thead th {
  background: var(--ds-surface-glass);
  color: var(--ds-ink-on-dark-muted);
  border-bottom-color: var(--ds-border-glass);
}

.ds-section--dark .ds-compare-table tbody th {
  color: var(--ds-ink-on-dark-strong);
}

.ds-section--dark .ds-compare-table .ds-compare-table__col--highlight {
  background: rgba(var(--ds-purple-rgb), 0.18);
  color: var(--ds-ink-on-dark-strong);
}

.ds-section--dark .ds-compare-table thead th.ds-compare-table__col--highlight {
  color: var(--ds-ink-on-dark-strong);
}

.ds-section--dark .ds-compare-caption {
  color: var(--ds-ink-on-dark-muted);
}

/* Mobile card collapse (<768px): the five provider columns cannot fit a
   phone, so each row becomes a stacked card of label/value pairs fed by the
   cells' data-col attributes. Scoped to .ds-compare-wrap (the marketing
   table); bare reference demos use the ds-table-wrap scroll strategy. */
@media (max-width: 767px) {
  .ds-compare-wrap .ds-compare-table,
  .ds-section--dark .ds-compare-wrap .ds-compare-table {
    display: flex;
    flex-direction: column;
    background: transparent;
    border: none;
    box-shadow: none;
    overflow: visible;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .ds-compare-wrap .ds-compare-table colgroup,
  .ds-compare-wrap .ds-compare-table thead {
    display: none;
  }
  .ds-compare-wrap .ds-compare-table tbody,
  .ds-compare-wrap .ds-compare-table tr,
  .ds-compare-wrap .ds-compare-table th,
  .ds-compare-wrap .ds-compare-table td {
    display: block;
    min-width: 0;
    width: auto;
  }
  .ds-compare-wrap .ds-compare-table tbody tr {
    background: var(--ds-surface);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-md);
    padding: var(--ds-space-4);
    margin-bottom: var(--ds-space-3);
  }
  .ds-compare-wrap .ds-compare-table tbody th {
    background: transparent;
    padding: 0 0 var(--ds-space-2);
    margin-bottom: var(--ds-space-2);
    font-size: var(--ds-text-body);
    border-bottom: 1px solid var(--ds-border-subtle);
  }
  .ds-compare-wrap .ds-compare-table tbody td {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--ds-space-4);
    padding: var(--ds-space-1-5) 0;
    border-bottom: none;
    text-align: right;
    background: transparent;
  }
  .ds-compare-wrap .ds-compare-table tbody td::before {
    content: attr(data-col);
    font-size: var(--ds-text-caption);
    font-weight: var(--ds-fw-semi);
    color: var(--ds-ink-mute);
    text-align: left;
    flex-shrink: 0;
  }
  .ds-compare-wrap .ds-compare-table tbody td.ds-compare-table__col--highlight {
    color: var(--ds-blue);
  }
  .ds-compare-wrap .ds-compare-caption {
    display: block;
    order: 1;
    padding: 0;
  }

  .ds-section--dark .ds-compare-wrap .ds-compare-table tbody tr {
    background: var(--ds-surface-glass);
    border-color: var(--ds-border-glass);
  }
  .ds-section--dark .ds-compare-wrap .ds-compare-table tbody th {
    border-bottom-color: var(--ds-border-glass);
  }
  .ds-section--dark .ds-compare-wrap .ds-compare-table tbody td {
    background: transparent;
  }
  .ds-section--dark .ds-compare-wrap .ds-compare-table tbody td::before {
    color: var(--ds-ink-on-dark-muted);
  }
  .ds-section--dark
    .ds-compare-wrap
    .ds-compare-table
    tbody
    td.ds-compare-table__col--highlight {
    color: var(--ds-ink-on-dark-strong);
  }
}

/* === Link-Card === */

.ds-link-card {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  padding: var(--ds-space-4);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-sm);
  text-decoration: none;
  color: var(--ds-ink);
  transition: all var(--ds-dur-base) var(--ds-ease-out);
  position: relative;
  overflow: hidden;
}

.ds-link-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--ds-gradient-brand);
  opacity: 0;
  transition: opacity var(--ds-dur-base) var(--ds-ease-out);
}

.ds-link-card:hover {
  transform: translateY(var(--ds-card-lift));
  box-shadow: var(--ds-shadow-lg);
  border-color: var(--ds-border);
  color: var(--ds-ink);
}

.ds-link-card:hover::before {
  opacity: 1;
}

.ds-link-card__icon {
  /* The shared gradient tile at the compact 40px size. */
  width: 40px;
  height: 40px;
  font-size: var(--ds-text-lg);
}

.ds-link-card__body {
  flex: 1;
  min-width: 0;
}

.ds-link-card__title {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0 0 var(--ds-space-0-5);
}

.ds-link-card__desc {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-soft);
  margin: 0;
}

/* === Home page ===
   The landing page: a dark hero with a floating product mockup, a fact
   strip that overlaps the hero edge, the numbered timeline, the sample
   records, the feature bento, pricing, the comparison on the dark stage,
   use cases, origin facts, FAQ and the closing CTA band. Light, tinted and
   dark sections alternate; every number in a mockup is labelled as a
   fictional example. Motion is the shared .ds-reveal fade plus one rise of
   the mockup on load. */

/* ---- Hero ---- */
.ds-home-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ds-gradient-mesh-dark);
  color: var(--ds-ink-on-dark-body);
  padding: clamp(3rem, 7vw, 6rem) 0
    calc(clamp(3rem, 6vw, 5rem) + var(--ds-space-16));
}

/* A violet glow behind the mockup, the brightest spot of the stage. */
.ds-home-hero::after {
  content: "";
  position: absolute;
  z-index: -1;
  top: 50%;
  right: -12%;
  width: min(900px, 90vw);
  aspect-ratio: 1;
  transform: translateY(-50%);
  background: radial-gradient(
    circle,
    rgba(var(--ds-purple-rgb), 0.3) 0%,
    rgba(var(--ds-blue-glow-rgb), 0.12) 38%,
    transparent 64%
  );
  pointer-events: none;
}

.ds-home-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-12);
  align-items: center;
}

@media (min-width: 992px) {
  .ds-home-hero__grid {
    grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.96fr);
    gap: var(--ds-space-16);
  }
}

.ds-home-hero__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.ds-home-hero__copy > .ds-eyebrow {
  margin-bottom: var(--ds-space-6);
}

.ds-home-hero__title {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-hero-dramatic);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-display);
  letter-spacing: var(--ds-tracking-display);
  color: var(--ds-ink-on-dark-strong);
  margin: 0 0 var(--ds-space-6);
}

/* The UTM hero variant marks its price with <em>: the on-dark gradient,
   upright, like the default gradient phrase. */
.ds-home-hero__title em {
  font-style: normal;
  background: var(--ds-gradient-text-on-dark);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* A hero intro line, not running text: the 36rem width is the layout of
   the hero column. */
.ds-home-hero__lead {
  max-width: 36rem;
  font-size: var(--ds-text-lead);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-on-dark-body);
  margin: 0 0 var(--ds-space-8);
}

.ds-home-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-3);
}

.ds-home-hero__micro {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2) var(--ds-space-5);
  margin: var(--ds-space-5) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-on-dark-body);
}

.ds-home-hero__micro li {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
}

.ds-home-hero__micro .bi {
  color: var(--ds-success-on-dark);
}

/* The live pool count as a glass chip. */
.ds-home-hero__proof {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-1) var(--ds-space-2-5);
  margin: var(--ds-space-8) 0 0;
  padding: var(--ds-space-2-5) var(--ds-space-4);
  background: var(--ds-surface-glass);
  border: 1px solid var(--ds-border-glass);
  border-radius: var(--ds-radius-full);
  box-shadow: var(--ds-shadow-pill-on-dark);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-on-dark-body);
}

.ds-home-hero__proof-value {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-black);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--ds-tracking-snug);
  color: var(--ds-ink-on-dark-strong);
}

.ds-home-hero__proof-label {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  padding-left: var(--ds-space-2-5);
  border-left: 1px solid var(--ds-border-glass);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-success-on-dark);
}

.ds-home-hero__proof-label::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-success-bright);
  box-shadow: 0 0 0 3px rgba(var(--ds-success-bright-rgb), 0.22);
}

@media (max-width: 575px) {
  .ds-home-hero__actions {
    align-self: stretch;
  }
  .ds-home-hero__actions .ds-button {
    flex: 1 1 100%;
  }
}

/* ---- Product mockup: a crop of the search with fictional data ---- */
.ds-preview {
  position: relative;
  margin: 0;
  padding: var(--ds-space-10) 0 var(--ds-space-12);
  animation: ds-rise 900ms var(--ds-ease-out) 120ms both;
}

.ds-preview__window {
  position: relative;
  background: var(--ds-surface);
  border-radius: var(--ds-radius-lg);
  box-shadow:
    0 0 0 1px rgba(var(--ds-white-rgb), 0.08),
    var(--ds-shadow-xl);
  overflow: hidden;
  color: var(--ds-ink);
}

.ds-preview__titlebar {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  padding: var(--ds-space-2-5) var(--ds-space-4);
  background: var(--ds-surface-tint);
  border-bottom: 1px solid var(--ds-border-subtle);
}

/* The three window dots, drawn by one pseudo-element and its shadows. */
.ds-preview__titlebar::before {
  content: "";
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  margin-right: var(--ds-space-8);
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-4);
  box-shadow:
    16px 0 0 var(--ds-surface-4),
    32px 0 0 var(--ds-surface-4);
}

.ds-preview__url {
  flex: 1;
  max-width: 260px;
  margin-inline: auto;
  padding: 3px var(--ds-space-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-full);
}

.ds-preview__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-3);
  padding: var(--ds-space-4) var(--ds-space-5);
  border-bottom: 1px solid var(--ds-border-subtle);
}

.ds-preview__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-1-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Filter chip: also used in the timeline crops and the branch examples. */
.ds-preview__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  padding: var(--ds-space-1) var(--ds-space-2-5);
  border: 1px solid rgba(var(--ds-blue-rgb), 0.18);
  border-radius: var(--ds-radius-full);
  background: var(--ds-neutral-tint-bg);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-neutral-tint-text);
  white-space: nowrap;
}

.ds-preview__count {
  margin: 0;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
}

.ds-preview__count strong {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-black);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-preview__rows {
  margin: 0;
  padding: var(--ds-space-1) 0;
  list-style: none;
}

.ds-preview__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--ds-space-3-5);
  padding: var(--ds-space-3) var(--ds-space-5);
}

.ds-preview__row + .ds-preview__row {
  border-top: 1px solid var(--ds-border-subtle);
}

.ds-preview__row.is-unlocked {
  background: linear-gradient(
    90deg,
    rgba(var(--ds-success-bright-rgb), 0.09) 0%,
    transparent 75%
  );
}

/* Score ring: the conic sweep shows the score out of ten. */
.ds-preview__ring {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--ds-radius-full);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-black);
  color: var(--ds-ink);
}

.ds-preview__ring--8 {
  background:
    radial-gradient(closest-side, var(--ds-surface) 76%, transparent 78%),
    conic-gradient(
      var(--ds-blue) 0%,
      var(--ds-purple) 80%,
      var(--ds-surface-3) 80% 100%
    );
}

.ds-preview__ring--7 {
  background:
    radial-gradient(closest-side, var(--ds-surface) 76%, transparent 78%),
    conic-gradient(
      var(--ds-blue) 0%,
      var(--ds-purple) 70%,
      var(--ds-surface-3) 70% 100%
    );
}

.ds-preview__ring--6 {
  background:
    radial-gradient(closest-side, var(--ds-surface) 76%, transparent 78%),
    conic-gradient(
      var(--ds-blue) 0%,
      var(--ds-purple) 60%,
      var(--ds-surface-3) 60% 100%
    );
}

.ds-preview__name {
  margin: 0;
  font-family: var(--ds-font-display);
  font-size: 0.9375rem;
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
}

.ds-preview__meta {
  margin: var(--ds-space-0-5) 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

.ds-preview__blur {
  filter: blur(5px);
  user-select: none;
}

.ds-preview__state {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  flex-shrink: 0;
  padding: var(--ds-space-0-5) var(--ds-space-2-5);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-full);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-mute);
}

/* Two small cards float off the window edges and add depth. */
.ds-preview__float {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--ds-space-2-5);
  padding: var(--ds-space-2-5) var(--ds-space-4) var(--ds-space-2-5)
    var(--ds-space-2-5);
  background: var(--ds-surface);
  border-radius: var(--ds-radius-md);
  box-shadow:
    0 0 0 1px rgba(var(--ds-ink-rgb), 0.04),
    var(--ds-shadow-float);
  color: var(--ds-ink);
  line-height: 1.3;
}

.ds-preview__float strong {
  display: block;
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-bold);
}

.ds-preview__float small {
  display: block;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

.ds-preview__float--count {
  top: 0;
  left: calc(-1 * var(--ds-space-6));
}

.ds-preview__float--export {
  right: calc(-1 * var(--ds-space-5));
  bottom: var(--ds-space-4);
}

.ds-preview__label {
  position: absolute;
  left: 0;
  bottom: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-on-dark-muted);
}

/* The floats stay inside the column on narrower screens; on a phone the
   preview shows two rows. */
@media (max-width: 1199px) {
  .ds-preview__float--count {
    left: var(--ds-space-3);
  }
  .ds-preview__float--export {
    right: var(--ds-space-3);
  }
}

@media (max-width: 575px) {
  .ds-preview__row--extra {
    display: none;
  }
  .ds-preview__row {
    padding-inline: var(--ds-space-4);
    gap: var(--ds-space-3);
  }
  .ds-preview__bar {
    padding-inline: var(--ds-space-4);
  }
  .ds-preview__float--export {
    display: none;
  }
}

/* ---- Fact strip: a white panel that overlaps the hero edge ---- */
.ds-facts {
  position: relative;
  z-index: 2;
  margin-top: calc(-1 * var(--ds-space-16));
}

.ds-facts__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin: 0;
  padding: var(--ds-space-2) var(--ds-space-5);
  list-style: none;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-lg);
}

.ds-facts__item {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3-5);
  padding: var(--ds-space-3-5) 0;
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  line-height: 1.4;
  color: var(--ds-ink);
}

.ds-facts__item + .ds-facts__item {
  border-top: 1px solid var(--ds-border-subtle);
}

@media (min-width: 576px) {
  .ds-facts__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--ds-space-8);
    padding: var(--ds-space-3) var(--ds-space-6);
  }
  .ds-facts__item:nth-child(2) {
    border-top: 0;
  }
}

@media (min-width: 1200px) {
  .ds-facts__list {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    column-gap: 0;
    padding: var(--ds-space-5) var(--ds-space-2);
  }
  .ds-facts__item {
    padding: var(--ds-space-1) var(--ds-space-6);
  }
  .ds-facts__item + .ds-facts__item {
    border-top: 0;
    border-left: 1px solid var(--ds-border-subtle);
  }
}

/* ---- Timeline: three numbered steps joined by a gradient line ---- */
.ds-steps {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-10);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 992px) {
  .ds-steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--ds-space-8);
  }
}

.ds-step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}

.ds-step__num {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--ds-space-2);
  border-radius: var(--ds-radius-md);
  background: var(--ds-gradient-icon-tile);
  box-shadow: var(--ds-shadow-icon-tile-lg);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-snug);
  color: var(--ds-white);
}

.ds-step__title {
  font-size: clamp(1.25rem, 1.15rem + 0.4vw, 1.4375rem);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0;
}

.ds-step__text {
  font-size: var(--ds-text-body);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-soft);
  margin: 0;
}

/* A small crop of the interface the step talks about, as a floating card. */
.ds-step__crop {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  margin-top: auto;
  padding: var(--ds-space-3-5) var(--ds-space-4);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-md);
}

.ds-step__count {
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
}

.ds-step__hint {
  display: inline-flex;
  align-items: center;
  padding: var(--ds-space-0-5) var(--ds-space-2-5);
  border-radius: var(--ds-radius-full);
  background: var(--ds-success-tint-bg);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-success-tint-text);
}

.ds-step__file {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  padding: var(--ds-space-1) var(--ds-space-2-5);
  border: 1px solid var(--ds-border-strong);
  border-radius: var(--ds-radius-xs);
  background: var(--ds-surface);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
}

.ds-step__file .bi {
  color: var(--ds-success);
}

/* The connector: horizontal between the tiles on desktop, vertical along
   the tile column below 992px. */
@media (min-width: 992px) {
  .ds-step:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 27px;
    left: 72px;
    right: calc(-1 * var(--ds-space-8) + var(--ds-space-4));
    height: 2px;
    border-radius: var(--ds-radius-full);
    background: linear-gradient(
      90deg,
      rgba(var(--ds-purple-rgb), 0.5),
      rgba(var(--ds-blue-rgb), 0.12)
    );
  }
}

@media (max-width: 991px) {
  .ds-step {
    padding-left: 76px;
  }
  .ds-step__num {
    position: absolute;
    top: 0;
    left: 0;
    margin: 0;
  }
  .ds-step__title {
    min-height: 56px;
    display: flex;
    align-items: center;
  }
  .ds-step:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 68px;
    bottom: calc(-1 * var(--ds-space-10) + var(--ds-space-3));
    left: 27px;
    width: 2px;
    border-radius: var(--ds-radius-full);
    background: linear-gradient(
      180deg,
      rgba(var(--ds-purple-rgb), 0.45),
      rgba(var(--ds-blue-rgb), 0.1)
    );
  }
}

.ds-filter-examples {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  margin: var(--ds-space-12) 0 0;
}

.ds-filter-examples__label {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-mute);
  margin-right: var(--ds-space-1);
}

/* ---- Sample records beside the field list ---- */
.ds-sample-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-10);
  align-items: start;
}

@media (min-width: 1200px) {
  .ds-sample-layout {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: var(--ds-space-12);
  }
}

.ds-field-list {
  padding: var(--ds-space-8);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-md);
}

@media (max-width: 575px) {
  .ds-field-list {
    padding: var(--ds-space-6) var(--ds-space-5);
  }
}

.ds-field-list__title {
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0 0 var(--ds-space-6);
}

.ds-field-list__items {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ds-field-list__items li {
  position: relative;
  padding-left: var(--ds-space-10);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-soft);
}

/* A white check on a gradient disc. */
.ds-field-list__items li::before {
  content: "\F272";
  position: absolute;
  left: 0;
  top: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-icon-tile);
  box-shadow: var(--ds-shadow-icon-tile);
  font-family: "bootstrap-icons";
  font-size: var(--ds-text-body-sm);
  line-height: 1;
  color: var(--ds-white);
}

.ds-field-list__items strong {
  display: block;
  margin-bottom: var(--ds-space-0-5);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
}

/* ---- Feature bento ----
   Six tiles on a 3-column grid: wide and tall tiles carry a small
   interface crop, the short ones icon, title and one line. The tall tile
   sits on the dark stage for contrast within the light section. */
.ds-bento {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-5);
}

@media (min-width: 768px) {
  .ds-bento {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .ds-bento__tile--wide {
    grid-column: 1 / -1;
  }
}

@media (min-width: 1100px) {
  .ds-bento {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-flow: dense;
  }
  .ds-bento__tile--wide {
    grid-column: span 2;
  }
  .ds-bento__tile--tall {
    grid-row: span 2;
  }
}

.ds-bento__tile {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-3);
  padding: var(--ds-space-8);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-sm);
  overflow: hidden;
}

@media (max-width: 575px) {
  .ds-bento__tile {
    padding: var(--ds-space-6) var(--ds-space-5);
  }
}

/* A faint brand glow in the top-right corner of every light tile. */
.ds-bento__tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(
    120% 80% at 100% 0%,
    rgba(var(--ds-purple-rgb), 0.07) 0%,
    transparent 55%
  );
  pointer-events: none;
}

.ds-bento__tile > .ds-icon-tile {
  margin-bottom: var(--ds-space-2);
}

.ds-bento__title {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0;
}

.ds-bento__text {
  font-size: 0.9375rem;
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-soft);
  margin: 0;
}

.ds-bento__visual {
  align-self: stretch;
  margin-top: auto;
  padding-top: var(--ds-space-4);
}

.ds-bento__tile--dark {
  background: var(--ds-gradient-mesh-dark);
  border-color: var(--ds-surface-dark-border);
  box-shadow: var(--ds-shadow-lg);
  color: var(--ds-ink-on-dark-body);
}

.ds-bento__tile--dark::before {
  background-image: var(--ds-pattern-grid-dark);
  background-size: 32px 32px;
  -webkit-mask-image: radial-gradient(
    ellipse 80% 60% at 70% 20%,
    var(--ds-black) 10%,
    transparent 70%
  );
  mask-image: radial-gradient(
    ellipse 80% 60% at 70% 20%,
    var(--ds-black) 10%,
    transparent 70%
  );
}

.ds-bento__tile--dark .ds-bento__title {
  color: var(--ds-ink-on-dark-strong);
}

.ds-bento__tile--dark .ds-bento__text {
  color: var(--ds-ink-on-dark-body);
}

/* Unlock crop: amount field, scope, sort chip and the action. */
.ds-bento__unlock {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2) var(--ds-space-3);
  padding: var(--ds-space-3) var(--ds-space-4);
  background: var(--ds-surface-tint);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-md);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
}

.ds-bento__field {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-width: 64px;
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-3);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-control);
  border-radius: var(--ds-radius-xs);
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-bento__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  padding: var(--ds-space-1) var(--ds-space-2-5);
  border-radius: var(--ds-radius-full);
  background: var(--ds-violet-tint-bg);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-violet-tint-text);
}

.ds-bento__action {
  display: inline-flex;
  align-items: center;
  min-height: var(--ds-control-h-sm);
  margin-left: auto;
  padding: 0 var(--ds-space-4);
  border-radius: var(--ds-radius-xs);
  background: var(--ds-gradient-cta);
  box-shadow: var(--ds-shadow-primary);
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-white);
}

/* Score bars in the dark tile; widths are the fictional example scores. */
.ds-bento__scores {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-4);
  padding: var(--ds-space-5);
  background: var(--ds-surface-glass);
  border: 1px solid var(--ds-border-glass);
  border-radius: var(--ds-radius-md);
}

.ds-bento__score {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--ds-space-2) var(--ds-space-3);
  align-items: center;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-on-dark-body);
}

.ds-bento__score strong {
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-black);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink-on-dark-strong);
}

.ds-bento__bar {
  grid-column: 1 / -1;
  height: 8px;
  border-radius: var(--ds-radius-full);
  background: rgba(var(--ds-white-rgb), 0.1);
  overflow: hidden;
}

.ds-bento__bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-meter-on-dark);
}

.ds-bento__bar-fill--6 {
  width: 60%;
}
.ds-bento__bar-fill--7 {
  width: 70%;
}
.ds-bento__bar-fill--8 {
  width: 80%;
}

.ds-bento__note {
  margin: 0;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-on-dark-muted);
}

/* Export crop: the file types and the first German column headers. */
.ds-bento__export {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}

.ds-bento__files {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.ds-bento__columns {
  display: flex;
  gap: 0;
  overflow: hidden;
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-sm);
  background: var(--ds-surface-tint);
  -webkit-mask-image: linear-gradient(
    90deg,
    var(--ds-black) 75%,
    transparent 100%
  );
  mask-image: linear-gradient(90deg, var(--ds-black) 75%, transparent 100%);
}

.ds-bento__columns > span {
  flex-shrink: 0;
  padding: var(--ds-space-2) var(--ds-space-4);
  border-right: 1px solid var(--ds-border);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-soft);
  white-space: nowrap;
}

/* Change crop: two updated fields as on the record's change table. */
.ds-bento__changes {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
}

.ds-bento__change {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-3);
  padding: var(--ds-space-2-5) var(--ds-space-3-5);
  background: var(--ds-surface-tint);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-sm);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
}

/* ---- Credit-Pakete panel and the action variant of the Free strip ---- */
.ds-plan-strip.ds-plan-strip--action {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-3) var(--ds-space-4);
  padding: var(--ds-space-4) var(--ds-space-5);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-sm);
}

.ds-plan-strip--action .ds-plan-strip__text {
  flex: 1 1 260px;
  font-size: var(--ds-text-body);
  color: var(--ds-ink-soft);
}

.ds-plan-strip--action .ds-plan-strip__text strong {
  font-family: var(--ds-font-display);
  color: var(--ds-ink);
}

.ds-topup-table {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-5);
  align-items: start;
  margin-top: var(--ds-space-10);
  padding: var(--ds-space-8);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-md);
}

@media (min-width: 992px) {
  .ds-topup-table {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    grid-template-areas:
      "intro table"
      "action table";
    column-gap: var(--ds-space-12);
  }
  .ds-topup-table > div:first-child {
    grid-area: intro;
  }
  .ds-topup-table > .ds-table-wrap {
    grid-area: table;
  }
  .ds-topup-table > .ds-button {
    grid-area: action;
    justify-self: start;
  }
}

/* Each figure stays on one line; on a phone the cells tighten and the
   column headers may wrap instead. */
.ds-topup-table .ds-table td {
  white-space: nowrap;
}

@media (max-width: 575px) {
  .ds-topup-table {
    padding: var(--ds-space-5) var(--ds-space-4);
  }
  .ds-topup-table .ds-table th {
    white-space: normal;
  }
  .ds-topup-table .ds-table th,
  .ds-topup-table .ds-table td {
    padding-inline: var(--ds-space-2-5);
  }
}

.ds-topup-table__title {
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0 0 var(--ds-space-3);
}

.ds-topup-table__lead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  margin: 0;
  font-size: var(--ds-text-body);
  color: var(--ds-ink-soft);
}

/* ---- Use cases and origin facts ---- */
.ds-usecase-grid,
.ds-origin-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-5);
}

@media (min-width: 768px) {
  .ds-usecase-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .ds-origin-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.ds-usecase-grid > .ds-card {
  padding: var(--ds-space-8);
  gap: var(--ds-space-3);
}

.ds-usecase-grid > .ds-card > .ds-card__icon {
  margin-bottom: var(--ds-space-2);
}

.ds-usecase__title,
.ds-origin__title {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0;
}

.ds-usecase__text,
.ds-origin__text {
  font-size: 0.9375rem;
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-soft);
  margin: 0;
}

.ds-origin {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-4);
  padding: var(--ds-space-6);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-sm);
}

.ds-origin__body {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  min-width: 0;
}

.ds-operator {
  margin: var(--ds-space-10) 0 0;
  text-align: center;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
}

/* === Sticky CTA ===
   One line at the bottom of the home page for visitors: "50 Credits gratis"
   and the signup button on a dark glass bar. home.js shows it only once the
   hero CTA has left the viewport and hides it again near the footer. */

.ds-sticky-cta {
  position: fixed;
  bottom: var(--ds-space-3);
  left: var(--ds-space-3);
  right: var(--ds-space-3);
  z-index: 40;
  padding: var(--ds-space-2) var(--ds-space-2) var(--ds-space-2)
    var(--ds-space-5);
  background: var(--ds-surface-dark-2);
  color: var(--ds-ink-on-dark-strong);
  border: 1px solid var(--ds-border-glass);
  border-radius: var(--ds-radius-lg);
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  box-shadow: var(--ds-shadow-xl);
  max-width: 520px;
  margin-inline: auto;
  transform: translateY(calc(100% + var(--ds-space-4)));
  transition: transform var(--ds-dur-base) var(--ds-ease-out);
}

/* The author display:flex above would otherwise defeat the hidden
   attribute the bar starts with. */
.ds-sticky-cta[hidden] {
  display: none;
}

.ds-sticky-cta.is-visible {
  transform: translateY(0);
}

.ds-sticky-cta.is-visible.is-near-footer {
  transform: translateY(calc(100% + var(--ds-space-4)));
}

.ds-sticky-cta-text {
  flex: 1;
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-bold);
}

.ds-sticky-cta-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: transparent;
  border: none;
  border-radius: var(--ds-radius-sm);
  color: var(--ds-ink-on-dark-muted);
  cursor: pointer;
}

.ds-sticky-cta-close:hover {
  background: var(--ds-surface-glass-strong);
  color: var(--ds-ink-on-dark-strong);
}

.ds-sticky-cta-close:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus-on-dark);
}

/* === Prose (blog articles and legal pages) ===
   Long-form text at 17px Inter with a 1.75 line height in --ds-ink-soft,
   headings in Jakarta, list bullets as small brand-gradient dots, tables on
   the neutral tint. Running text takes the full width of its column; the
   distance to the edge comes from the container padding alone. Headings
   leave room under the sticky header when a TOC link jumps to them. */

.ds-prose {
  font-size: 1.0625rem;
  line-height: 1.75;
  color: var(--ds-ink-soft);
}

.ds-prose h2,
.ds-prose h3 {
  font-family: var(--ds-font-display);
  color: var(--ds-ink);
  letter-spacing: var(--ds-tracking-tight);
  scroll-margin-top: calc(var(--ds-site-header-h) + var(--ds-space-6));
}

.ds-prose h2 {
  margin: var(--ds-space-12) 0 var(--ds-space-4);
  font-size: clamp(1.375rem, 1.22rem + 0.6vw, 1.625rem);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-snug);
}
.ds-prose h3 {
  margin: var(--ds-space-8) 0 var(--ds-space-3);
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  line-height: var(--ds-lh-snug);
}
.ds-prose > :first-child {
  margin-top: 0;
}
.ds-prose p {
  margin: 0 0 var(--ds-space-5);
}
.ds-prose ul,
.ds-prose ol {
  margin: 0 0 var(--ds-space-5);
  padding-left: var(--ds-space-6);
}
.ds-prose li {
  margin-bottom: var(--ds-space-2);
}
.ds-prose li > ul,
.ds-prose li > ol {
  margin: var(--ds-space-2) 0 0;
}
.ds-prose ul {
  list-style: none;
}
.ds-prose ul > li {
  position: relative;
}
.ds-prose ul > li::before {
  content: "";
  position: absolute;
  top: 0.68em;
  left: calc(-1 * var(--ds-space-4-5));
  width: 7px;
  height: 7px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-cta);
}
.ds-prose ol > li::marker {
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-blue);
}
.ds-prose a {
  color: var(--ds-blue);
  text-decoration: underline;
  text-decoration-color: rgba(var(--ds-blue-rgb), 0.4);
  text-underline-offset: 3px;
}
.ds-prose a:hover {
  text-decoration-color: currentColor;
}
.ds-prose strong {
  color: var(--ds-ink);
  font-weight: var(--ds-fw-semi);
}
.ds-prose blockquote {
  margin: var(--ds-space-8) 0;
  padding: var(--ds-space-5) var(--ds-space-6);
  background: var(--ds-surface-tint);
  border-left: 4px solid var(--ds-purple);
  border-radius: 0 var(--ds-radius-md) var(--ds-radius-md) 0;
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink);
}
.ds-prose blockquote > :last-child {
  margin-bottom: 0;
}
.ds-prose hr {
  margin: var(--ds-space-10) 0;
  border: 0;
  border-top: 1px solid var(--ds-border);
  opacity: 1;
}

/* Prose tables — blog-article bodies author bare <table> elements (no class),
   which would otherwise render as unstyled browser-default tables clashing
   with the surrounding prose. They mirror the .ds-table skin: header on the
   neutral tint, hairline rows, rounded frame. */
.ds-prose table {
  width: 100%;
  margin: 0 0 var(--ds-space-6);
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  overflow: hidden;
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
}
/* Below the tablet breakpoint the article comparison tables are wider than
   the prose column (measured: 566-629px intrinsic in a 327px container)
   (WCAG 1.4.10 Reflow). Every article table therefore sits in a
   `.ds-prose-table` wrapper that scrolls instead of the table itself: the
   table keeps `display: table` and its row/cell semantics in every engine,
   and the wrapper carries `tabindex="0"`, `role="region"` and an
   `aria-label` in the article file, so keyboard users can focus it and
   scroll the overflow in WebKit too, where scrollers are not focusable on
   their own. The table cannot shrink below its content width, so on a phone
   it overflows the wrapper and the wrapper scrolls. */
.ds-prose-table {
  margin: 0 0 var(--ds-space-6);
  overflow-x: auto;
}
.ds-prose-table > table {
  margin: 0;
}
.ds-prose-table:focus-visible {
  outline: 3px solid var(--ds-blue);
  outline-offset: 3px;
}

.ds-prose th,
.ds-prose td {
  padding: var(--ds-space-3) var(--ds-space-4);
  text-align: left;
  vertical-align: top;
}
.ds-prose thead th {
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-ink);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-bold);
  border-bottom: 1px solid var(--ds-border);
}
.ds-prose tbody td {
  color: var(--ds-ink-soft);
  border-bottom: 1px solid var(--ds-border-subtle);
}
.ds-prose tbody tr:nth-child(even) td {
  background: var(--ds-surface-tint);
}
.ds-prose tbody tr:last-child td {
  border-bottom: 0;
}

/* Legal-prose modifier — the white document card the legal pages sit in,
   with a hairline above every h2 after the first so a long document scans
   as numbered chapters. */
.ds-prose--legal {
  padding: clamp(1.5rem, 4vw, 3.5rem);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-md);
}

.ds-prose--legal > :first-child {
  margin-top: 0;
}
.ds-prose--legal > :last-child {
  margin-bottom: 0;
}

.ds-prose--legal h2 {
  margin-top: var(--ds-space-10);
  padding-top: var(--ds-space-8);
  border-top: 1px solid var(--ds-border-subtle);
}

.ds-prose--legal h2:first-of-type {
  padding-top: 0;
  border-top: none;
  margin-top: var(--ds-space-6);
}

.ds-prose--legal hr {
  margin: var(--ds-space-8) 0;
  border-top-style: dashed;
}

/* Inline metadata line (Stand: date, last-updated notice) used at the top
   of legal documents, set as a small pill. */
.ds-prose-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-1-5) var(--ds-space-3);
  background: var(--ds-surface-tint);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-full);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
}

/* === Document layout with a sticky table of contents ===
   Legal pages and blog articles: from 1200px a 248px contents column sits
   beside the text and follows it down the page; below that the column is
   gone and the text takes the full width. static/js/toc.js fills the list
   from the document's h2 headings and marks the section in view; without
   script, or with fewer than three headings, the nav stays hidden. */
.ds-doc {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-12);
  align-items: start;
}

@media (min-width: 1200px) {
  .ds-doc:has(> .ds-toc:not([hidden])) {
    grid-template-columns: 248px minmax(0, 1fr);
  }
}

.ds-doc__body {
  min-width: 0;
}

.ds-toc {
  display: none;
}

@media (min-width: 1200px) {
  .ds-toc:not([hidden]) {
    display: block;
    position: sticky;
    top: calc(var(--ds-site-header-h) + var(--ds-space-6));
    max-height: calc(100vh - var(--ds-site-header-h) - var(--ds-space-12));
    overflow-y: auto;
    padding: var(--ds-space-5);
    background: var(--ds-surface);
    border: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-lg);
    box-shadow: var(--ds-shadow-sm);
    scrollbar-width: thin;
  }
}

.ds-toc__title {
  margin: 0 0 var(--ds-space-3);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-fw-bold);
  letter-spacing: var(--ds-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ds-blue-dark);
}

.ds-toc__list {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-0-5);
  margin: 0;
  padding: 0;
  list-style: none;
  border-left: 2px solid var(--ds-border-subtle);
}

.ds-toc__link {
  position: relative;
  display: block;
  margin-left: -2px;
  padding: var(--ds-space-1-5) var(--ds-space-2) var(--ds-space-1-5)
    var(--ds-space-3-5);
  border-left: 2px solid transparent;
  font-size: var(--ds-text-body-sm);
  line-height: 1.4;
  color: var(--ds-ink-mute);
  text-decoration: none;
  transition:
    color var(--ds-dur-fast) var(--ds-ease-standard),
    border-color var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-toc__link:hover {
  color: var(--ds-ink);
}

.ds-toc__link.is-active {
  border-left-color: var(--ds-purple);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
}

.ds-toc__link:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
  border-radius: var(--ds-radius-xs);
}

/* === Site Header ===
   A sticky, translucent white bar (72px on marketing pages, 64px in the
   app) with a 16px backdrop blur. Jakarta text navigation without icons;
   the current page or the section in view carries a short brand-gradient
   underline. */

.ds-site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(var(--ds-white-rgb), 0.84);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid rgba(var(--ds-ink-rgb), 0.07);
}

@supports not (backdrop-filter: blur(16px)) {
  .ds-site-header {
    background: var(--ds-surface);
  }
}

.ds-site-header__inner {
  display: flex;
  align-items: center;
  /* Brand and actions are both flex-shrink:0, so the gap and the inline
     padding scale fluidly instead of stepping at a breakpoint: the row has
     to give continuously down to 320px. */
  gap: clamp(var(--ds-space-2), 2.5vw, var(--ds-space-5));
  min-height: var(--ds-site-header-h);
  padding: 0 var(--ds-gutter);
  max-width: 1280px;
  margin: 0 auto;
}

.ds-site-header__inner--centered {
  justify-content: center;
}

.ds-site-header__brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  gap: var(--ds-space-2-5);
  font-family: var(--ds-font-display);
  font-size: 1.1875rem;
  font-weight: var(--ds-fw-black);
  color: var(--ds-ink);
  letter-spacing: var(--ds-tracking-tight);
  text-decoration: none;
  flex-shrink: 0;
}

a.ds-site-header__brand:hover {
  color: var(--ds-ink);
}

.ds-site-header__logo {
  width: 30px;
  height: 30px;
}

/* Brand text next to the logo — inherits the brand link color/weight. */
.ds-site-header__wordmark {
  white-space: nowrap;
}

.ds-site-nav {
  display: flex;
  align-items: center;
  gap: var(--ds-space-1);
  flex: 1;
  justify-content: center;
}

.ds-site-nav__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--ds-space-3-5);
  font-family: var(--ds-font-display);
  font-size: 0.9375rem;
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-soft);
  text-decoration: none;
  border-radius: var(--ds-radius-sm);
  transition:
    color var(--ds-dur-fast) var(--ds-ease-standard),
    background-color var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-site-nav__item::after {
  content: "";
  position: absolute;
  left: var(--ds-space-3-5);
  right: var(--ds-space-3-5);
  bottom: 6px;
  height: 2px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-brand);
  opacity: 0;
  transform: scaleX(0.4);
  transition:
    opacity var(--ds-dur-base) var(--ds-ease-out),
    transform var(--ds-dur-base) var(--ds-ease-out);
}

.ds-site-nav__item:hover {
  color: var(--ds-ink);
}

.ds-site-nav__item[aria-current="page"],
.ds-site-nav__item.is-active {
  color: var(--ds-ink);
}

.ds-site-nav__item[aria-current="page"]::after,
.ds-site-nav__item.is-active::after {
  opacity: 1;
  transform: none;
}

.ds-site-nav__item:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* Below 992px the nav folds into the burger: a full-width stack under the
   header. */
@media (max-width: 991px) {
  .ds-site-nav {
    display: none;
  }
  .ds-site-nav.is-open {
    display: flex;
    position: fixed;
    top: var(--ds-site-header-h);
    left: 0;
    right: 0;
    background: var(--ds-surface);
    padding: var(--ds-space-3) var(--ds-gutter) var(--ds-space-5);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    border-bottom: 1px solid var(--ds-border);
    box-shadow: var(--ds-shadow-lg);
    z-index: 99;
  }
  .ds-site-nav.is-open .ds-site-nav__item {
    padding: 0 var(--ds-space-2);
    font-size: var(--ds-text-body);
    min-height: 52px;
    border-bottom: 1px solid var(--ds-border-subtle);
    border-radius: 0;
  }
  .ds-site-nav.is-open .ds-site-nav__item:last-child {
    border-bottom: 0;
  }
  .ds-site-nav.is-open .ds-site-nav__item::after {
    display: none;
  }
  .ds-site-nav.is-open .ds-site-nav__item.is-active,
  .ds-site-nav.is-open .ds-site-nav__item[aria-current="page"] {
    color: var(--ds-blue);
  }
}

.ds-site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  flex-shrink: 0;
  /* On app pages the nav block is omitted from the DOM; auto margin keeps
     the actions right-aligned in both layouts. */
  margin-left: auto;
}

/* "Anmelden" reads as quiet ink next to the gradient CTA. */
.ds-site-header__actions .ds-button--ghost {
  color: var(--ds-ink);
}

.ds-site-header__actions .ds-button--ghost:hover {
  color: var(--ds-blue-dark);
}

/* On a phone only the primary CTA and the burger stay in the bar; the
   login link moves into the burger menu (.ds-site-nav carries its own
   d-sm-none entry). */
@media (max-width: 575px) {
  .ds-site-header__actions .ds-button--ghost {
    display: none;
  }
  .ds-site-header__actions {
    gap: var(--ds-space-1-5);
  }
  .ds-site-header__actions .ds-button--sm {
    padding-inline: var(--ds-space-3);
  }
}

.ds-wallet-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-3);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-strong);
  border-radius: var(--ds-radius-sm);
  text-decoration: none;
  transition:
    background-color var(--ds-dur-fast) var(--ds-ease-standard),
    border-color var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-wallet-badge > .bi {
  color: var(--ds-blue);
}

a.ds-wallet-badge:hover {
  background: var(--ds-surface-tint);
  border-color: var(--ds-border-control);
  color: var(--ds-ink);
}

.ds-wallet-badge:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* Credit count inside the badge — tabular digits keep the header steady
   while the balance changes width. */
.ds-wallet-badge__value {
  font-variant-numeric: tabular-nums;
}

.ds-wallet-badge--low {
  background: var(--ds-warn-tint-bg);
  border-color: transparent;
  color: var(--ds-warn-tint-text);
}

.ds-wallet-badge--critical {
  background: var(--ds-danger-tint-bg);
  border-color: transparent;
  color: var(--ds-danger-tint-text);
}

.ds-wallet-badge--low > .bi,
.ds-wallet-badge--critical > .bi {
  color: inherit;
}

/* On desktop the sidebar wallet card is the one credit display; the header
   pill only serves the phone and tablet layout, where the sidebar is an
   off-canvas drawer or an icon rail. */
@media (min-width: 1200px) {
  .ds-layout-shell--app .ds-site-header__actions .ds-wallet-badge {
    display: none;
  }
}

.ds-avatar-menu {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-icon-tile);
  color: var(--ds-white);
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-bold);
  font-size: var(--ds-text-body-sm);
  border: 0;
  cursor: pointer;
  text-decoration: none;
  box-shadow: var(--ds-shadow-sm);
  transition: box-shadow var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-avatar-menu:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus-on-brand);
}

a.ds-avatar-menu:hover,
.ds-avatar-menu:hover {
  color: var(--ds-white);
  box-shadow: var(--ds-shadow-primary-hover);
}

.ds-site-header__burger {
  display: none;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 0;
  border-radius: var(--ds-radius-sm);
  cursor: pointer;
  padding: 0;
  color: var(--ds-ink);
}

.ds-site-header__burger:hover {
  background: var(--ds-surface-tint);
}

.ds-site-header__burger:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

@media (max-width: 991px) {
  .ds-site-header__burger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

@media (max-width: 767px) {
  .ds-site-header__actions .ds-wallet-badge__label {
    display: none;
  }
}

.ds-site-header--minimal {
  position: static;
  background: var(--ds-surface);
}

/* App shell: a 64px bar whose brand block is exactly as wide as the sidebar,
   so the logo sits above the navigation column. */
.ds-layout-shell--app .ds-site-header__inner {
  max-width: none;
  min-height: var(--ds-app-header-h);
  padding-left: 0;
}

.ds-layout-shell--app .ds-site-header__brand {
  width: var(--ds-sidebar-width);
  padding-left: var(--ds-space-5);
}

@media (max-width: 1199px) {
  .ds-layout-shell--app .ds-site-header__brand {
    width: auto;
    padding-left: var(--ds-gutter);
  }
}

/* === Site Footer ===
   Marketing footer on the dark stage: near-black navy lit by two faint
   brand glows, a brand-gradient hairline on top, the brand column with one
   line of verifiable facts, three link columns plus contact, and the legal
   strip. No seal-like chips. The app and auth shells use the light minimal
   variant at the end of the block. */

.ds-site-footer {
  position: relative;
  background:
    radial-gradient(
      55% 60% at 12% 0%,
      rgba(var(--ds-blue-glow-rgb), 0.12) 0%,
      transparent 70%
    ),
    radial-gradient(
      45% 55% at 92% 8%,
      rgba(var(--ds-purple-rgb), 0.14) 0%,
      transparent 70%
    ),
    var(--ds-surface-dark);
  color: var(--ds-ink-on-dark-body);
}

.ds-site-footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--ds-gradient-brand);
  opacity: 0.7;
}

.ds-site-footer__main {
  padding: clamp(48px, 7vw, 80px) var(--ds-gutter) var(--ds-space-6);
  max-width: 1280px;
  margin: 0 auto;
}

/* After the CTA band a hairline separates the band from the link grid. */
.ds-cta-band + .ds-site-footer__main {
  border-top: 1px solid var(--ds-surface-dark-border);
}

.ds-site-footer__grid {
  display: grid;
  gap: var(--ds-space-10) var(--ds-space-6);
  grid-template-columns: 1fr;
}

@media (min-width: 576px) {
  .ds-site-footer__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 992px) {
  .ds-site-footer__grid {
    grid-template-columns: minmax(0, 1.7fr) repeat(4, minmax(0, 1fr));
  }
}

/* ---- Brand column ---- */
.ds-site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-4);
  padding-right: var(--ds-space-6);
}

@media (min-width: 576px) and (max-width: 991px) {
  .ds-site-footer__brand {
    grid-column: 1 / -1;
  }
}

.ds-site-footer__brand .ds-site-footer__wordmark-row {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2-5);
  font-family: var(--ds-font-display);
  font-size: 1.25rem;
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink-on-dark-strong);
  margin: 0;
}

/* The logo sits on a white tile, so its blue and orange-red read on the
   dark ground exactly as on the light pages. */
.ds-site-footer__wordmark-row img {
  width: 36px;
  height: 36px;
  padding: 5px;
  background: var(--ds-white);
  border-radius: var(--ds-radius-sm);
  box-shadow: var(--ds-shadow-icon-tile);
}

.ds-site-footer__brand p {
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-on-dark-body);
  margin: 0;
}

.ds-site-footer__brand .ds-site-footer__facts {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-on-dark-muted);
}

/* ---- Link columns ---- */
.ds-site-footer__col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-1);
}

.ds-site-footer__heading {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-eyebrow);
  font-weight: var(--ds-fw-bold);
  letter-spacing: var(--ds-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ds-ink-on-dark-accent);
  margin: 0 0 var(--ds-space-3);
}

.ds-site-footer__heading::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-gradient-cta);
}

.ds-site-footer__col a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-on-dark-body);
  text-decoration: none;
  transition: color var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-site-footer__col a:hover {
  color: var(--ds-ink-on-dark-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.ds-site-footer__address {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-on-dark-muted);
  margin: var(--ds-space-1) 0 0;
}

/* ---- Legal strip ---- */
.ds-site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-3) var(--ds-space-6);
  margin-top: var(--ds-space-12);
  padding-top: var(--ds-space-5);
  border-top: 1px solid var(--ds-surface-dark-border);
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-on-dark-muted);
}

.ds-site-footer__legal-entity {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-1) var(--ds-space-2);
}

.ds-site-footer__legal-entity .ds-site-footer__sep {
  color: var(--ds-ink-on-dark-subtle);
}

.ds-site-footer__legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-1) var(--ds-space-4);
}

.ds-site-footer__legal-links a,
.ds-site-footer__legal-links .ds-footer-link-button {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--ds-ink-on-dark-body);
  text-decoration: none;
  font-size: var(--ds-text-caption);
}

.ds-site-footer__legal-links a:hover,
.ds-site-footer__legal-links .ds-footer-link-button:hover {
  color: var(--ds-ink-on-dark-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Footer consent re-open control — a <button> (it triggers JS, not a
   navigation), styled to read exactly like its link neighbours. */
.ds-footer-link-button {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

.ds-site-footer--minimal::before {
  display: none;
}

.ds-site-footer--minimal {
  background: var(--ds-surface);
  color: var(--ds-ink-mute);
  border-top: 1px solid var(--ds-border-subtle);
  padding: var(--ds-space-4) var(--ds-gutter);
}

.ds-app-footer__inner {
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-4);
  flex-wrap: wrap;
}

.ds-app-footer__copyright {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

.ds-app-footer__links {
  display: flex;
  gap: var(--ds-space-4);
  flex-wrap: wrap;
}

.ds-app-footer__links a,
.ds-app-footer__links .ds-footer-link-button {
  color: var(--ds-ink-soft);
  text-decoration: none;
  font-size: var(--ds-text-caption);
  transition: color var(--ds-dur-fast) var(--ds-ease-out);
  /* Same SC 2.5.8 floor as the marketing footer's legal row. */
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

.ds-app-footer__links a:hover,
.ds-app-footer__links a:focus-visible,
.ds-app-footer__links .ds-footer-link-button:hover,
.ds-app-footer__links .ds-footer-link-button:focus-visible {
  color: var(--ds-blue);
}

@media (max-width: 575px) {
  .ds-app-footer__inner {
    justify-content: center;
    text-align: center;
  }
  /* Center the wrapping link rows too, so a wrapped link ("Support") does
       not orphan left-flush under the centered copyright line. */
  .ds-app-footer__links {
    justify-content: center;
  }
}

/* === Consent banner (self-hosted DSGVO cookie consent) === */

.ds-consent {
  position: fixed;
  bottom: var(--ds-space-4);
  left: var(--ds-space-4);
  right: var(--ds-space-4);
  max-width: 520px;
  margin: 0 auto;
  /* Below the modal layers: an open confirm dialog or filter sheet must
       win against the cookie banner. */
  z-index: var(--ds-z-consent);
  outline: none;
}

.ds-consent[hidden] {
  display: none;
}

.ds-consent__card {
  padding: var(--ds-space-4) var(--ds-space-5);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-lg);
}

.ds-consent__title {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  margin: 0 0 var(--ds-space-2);
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
}

.ds-consent__title .bi {
  color: var(--ds-blue);
}

.ds-consent__text {
  margin: 0 0 var(--ds-space-4);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-prose);
  color: var(--ds-ink-soft);
}

.ds-consent__text a {
  color: var(--ds-blue);
}

.ds-consent__actions {
  display: flex;
  gap: var(--ds-space-3);
  flex-wrap: wrap;
}

@media (max-width: 575px) {
  .ds-consent__actions {
    flex-direction: column;
  }
  .ds-consent__actions .ds-button {
    width: 100%;
    justify-content: center;
  }
}

/* ============================================================================
 * DS CONTRAST FIXES — Post-Migration Polish
 * Context-overrides for cases where class-inheritance led to contrast bugs.
 * ========================================================================== */

/* Compare-table cell marks: a green check, a muted cross (the 3:1 icon
   floor rules out the dimmer ink), muted prose. On the dark stage the
   on-dark set takes over: check 9.95:1, cross 5.06:1, prose 8.01:1. */
.ds-compare-table td span,
.ds-compare-table td small {
  color: inherit;
}
.ds-compare-table .ds-compare-yes {
  color: var(--ds-success);
  font-weight: var(--ds-fw-bold);
}
.ds-compare-table .ds-compare-no {
  color: var(--ds-ink-mute);
}
.ds-compare-table .ds-compare-mid {
  color: var(--ds-ink-mute);
}
.ds-section--dark .ds-compare-table .ds-compare-yes {
  color: var(--ds-success-on-dark);
}
.ds-section--dark .ds-compare-table .ds-compare-no {
  color: var(--ds-ink-on-dark-subtle);
}
.ds-section--dark .ds-compare-table .ds-compare-mid {
  color: var(--ds-ink-on-dark-muted);
}

/* Text links in running text on the dark stage: white and underlined. The
   extra class outranks the light `p a` link rule further down. Breadcrumb
   and footer links keep their own colours. */
.ds-cta-band p a:not(.ds-button):not(.ds-badge),
.ds-home-hero p a:not(.ds-button):not(.ds-badge),
.ds-page-hero p a:not(.ds-button):not(.ds-badge),
.ds-section--dark p a:not(.ds-button):not(.ds-badge),
.ds-section--dark li a:not(.ds-button):not(.ds-badge) {
  color: var(--ds-white);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ============================================================================
 * App components — the authenticated area
 * App layout · Unlock Bar · Lead Table · Records View · Plan-Status Card ·
 * App Hero · Filter Bar
 * ============================================================================ */

/* === App page header ===
   Replaces the marketing hero on every /app/ page: a 24px H1, one muted
   line, the page's actions on the right. No eyebrow, no gradient word, no
   back button where the sidebar already shows the location. Detail pages
   add a text breadcrumb above the title. */
.ds-app-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ds-space-4);
  padding: var(--ds-space-2) 0 var(--ds-space-5);
}

.ds-app-header__text {
  min-width: 0;
  flex: 1 1 320px;
}

.ds-app-header__title {
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-page-title);
  font-weight: var(--ds-fw-black);
  line-height: 1.25;
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
  margin: 0;
}

.ds-app-header__desc {
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
  margin: var(--ds-space-1) 0 0;
}

.ds-app-header__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.ds-app-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-1-5);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
  margin: 0 0 var(--ds-space-2);
}

.ds-app-breadcrumb a {
  color: var(--ds-blue);
  text-decoration: none;
}

.ds-app-breadcrumb a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* === App-Layout-Container ===
   Legacy `min-height: calc(100vh - 64px)` removed: it was a holdover from an
   old fixed top-bar that subtracted itself from the body. The current header
   participates in the body's flex column, so it does not need to be
   subtracted. The sticky-footer pattern lives on `.ds-layout-shell`
   (`min-height: 100vh; display: flex; flex-direction: column`). */
.app-shell {
  background: var(--ds-surface-tint);
}
.app-container {
  width: 100%;
  max-width: 1320px;
  min-width: 0;
  margin: 0 auto;
  /* Symmetric 24px top/bottom; zero horizontal so content reaches the
       container edge on wide screens. On viewports narrower than the
       1320px max-width, a 12px safety padding kicks in (see media query
       below) to absorb the negative margins of Bootstrap .row elements. */
  padding: var(--ds-space-6) 0;
}

/* Content-driven exception: matches the 1320px .app-container cap — below it
   the side gutters collapse to half a Bootstrap gutter. Expressed in rem, not
   as the literal 12px it resolves to at the default root size: the gutter it
   compensates is itself rem-based (--bs-gutter-x: 1.5rem), so a reader who
   raises the browser's base font grew the negative margin while a px padding
   stayed put — 6px of every app page was clipped at a 24px root. */
@media (max-width: 1320px) {
  .app-container {
    padding-inline: 0.75rem;
  }
}

/* On app-shell pages the sidebar offsets the content area to the right, so
   the container no longer sits centered with viewport-side margin. Force
   the 12px Bootstrap-gutter-compensation padding on every breakpoint to
   prevent row negative margins from triggering horizontal scroll. */
body.ds-layout-shell--app .app-container {
  padding-inline: 0.75rem;
}

/* ============================================================================
 * Lead-Search — Unlock-Bar (Top-N input) + Table/Row-list layout
 * One compact row above the table on desktop: "[25] von 1.234 Treffern
 * freischalten · 25 Credits · Guthaben 488 [Freischalten]". Below 768px the
 * same form is fixed to the bottom of the viewport so the action stays in
 * reach while the user scrolls the cards.
 * ========================================================================== */

.ds-unlock-bar {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  padding: var(--ds-space-3) var(--ds-space-4);
  margin-bottom: var(--ds-space-4);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
}

.ds-unlock-bar__row {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
}

.ds-unlock-bar__input {
  flex: 0 0 auto;
  width: 96px;
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.ds-unlock-bar__summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--ds-space-1) var(--ds-space-3);
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
  font-variant-numeric: tabular-nums;
}

.ds-unlock-bar__of {
  color: var(--ds-ink);
  font-weight: var(--ds-fw-medium);
}

.ds-unlock-bar__cost strong {
  color: var(--ds-ink);
  font-weight: var(--ds-fw-semi);
}

.ds-unlock-bar__submit {
  flex-shrink: 0;
}

.ds-unlock-bar__note {
  margin: 0;
}

@media (max-width: 767px) {
  /* Fixed rather than sticky: the form sits above the cards in the DOM, and
     a sticky element only sticks while its own position is still ahead. The
     results column reserves the bar's height so the last card stays
     reachable; z-index stays below the sidebar drawer (90) and its
     backdrop (89). */
  .ds-unlock-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 80;
    margin: 0;
    padding: var(--ds-space-3) var(--ds-gutter)
      calc(var(--ds-space-3) + env(safe-area-inset-bottom, 0px));
    border-width: 1px 0 0;
    border-radius: 0;
    box-shadow: var(--ds-shadow-lg);
  }
  .ds-search-results:has(.ds-unlock-bar) {
    padding-bottom: 112px;
  }
  .ds-unlock-bar__row {
    flex-wrap: wrap;
  }
  .ds-unlock-bar__input {
    width: 84px;
  }
  .ds-unlock-bar__summary {
    flex: 1 1 140px;
    flex-direction: column;
    gap: 0;
  }
  .ds-unlock-bar__submit {
    flex: 1 1 100%;
  }
}

@media (min-width: 420px) and (max-width: 767px) {
  .ds-unlock-bar__submit {
    flex: 0 0 auto;
  }
}

.ds-perpage-form {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  margin: 0;
}
.ds-perpage-form select {
  width: auto;
  min-width: 70px;
}

/* ============================================================================
 * Lead-Table — dense row-list layout (50-100 records per view)
 * ========================================================================== */

.ds-lead-table-wrap {
  border-radius: var(--ds-radius-md);
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
  border: 1px solid var(--ds-border);
}

.ds-lead-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ds-text-body-sm);
}

.ds-lead-table thead {
  background: var(--ds-surface-tint);
  border-bottom: 1px solid var(--ds-border);
}

/* 12px side padding: at 1440 px with the sidebar open, 16px pushed the
   seven columns plus the row's unlock button past the content width. */
.ds-lead-table th {
  padding: var(--ds-space-3);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-mute);
  text-transform: none;
  letter-spacing: 0;
  text-align: left;
  white-space: nowrap;
}

.ds-lead-table td {
  padding: var(--ds-space-3);
  border-bottom: 1px solid var(--ds-border-subtle);
  color: var(--ds-ink);
  font-variant-numeric: tabular-nums;
  vertical-align: middle;
}

.ds-lead-table tbody tr {
  transition: background var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-lead-table tbody tr:hover {
  background: var(--ds-surface-tint);
}
.ds-lead-table tbody tr:last-child td {
  border-bottom: 0;
}

.ds-lead-table__company {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2-5);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  white-space: nowrap;
}
.ds-lead-table__company .bi {
  font-size: 1.05rem;
}

.ds-lead-table__muted {
  color: var(--ds-ink-mute);
  font-weight: var(--ds-fw-medium);
}

/* Contact availability: three labelled icons; present ones in the status
   green, absent ones in the dim ink (icons, never text, so 3:1 suffices). */
.ds-lead-table__icons {
  display: inline-flex;
  gap: var(--ds-space-2-5);
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--ds-text-body);
}

.ds-lead-table__icons .is-present {
  color: var(--ds-success);
}

.ds-lead-table__icons .is-absent {
  color: var(--ds-ink-dim);
}

/* One-line cells: location, size and revenue never wrap; the full value
   stays in the cell's title attribute. */
.ds-lead-table__cell--location,
.ds-lead-table__cell--size,
.ds-lead-table__cell--revenue {
  white-space: nowrap;
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Buying-readiness meter in the search table: the 1-10 value in tabular
   figures beside a ten-step bar. The fill width comes from one of ten
   modifier classes, so no inline style is needed. */
.ds-score-meter {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  white-space: nowrap;
}

.ds-score-meter__value {
  min-width: 1.25em;
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--ds-ink);
}

.ds-score-meter__bar {
  position: relative;
  display: block;
  width: 56px;
  height: 6px;
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-3);
  overflow: hidden;
}

.ds-score-meter__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: inherit;
  background: var(--ds-gradient-meter);
}

.ds-score-meter__fill--1 {
  width: 10%;
}
.ds-score-meter__fill--2 {
  width: 20%;
}
.ds-score-meter__fill--3 {
  width: 30%;
}
.ds-score-meter__fill--4 {
  width: 40%;
}
.ds-score-meter__fill--5 {
  width: 50%;
}
.ds-score-meter__fill--6 {
  width: 60%;
}
.ds-score-meter__fill--7 {
  width: 70%;
}
.ds-score-meter__fill--8 {
  width: 80%;
}
.ds-score-meter__fill--9 {
  width: 90%;
}
.ds-score-meter__fill--10 {
  width: 100%;
}

/* Neutral lock state in the search table's last column. */
.ds-lead-table__locked {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  white-space: nowrap;
}

.ds-lead-table .ds-lead-contact {
  filter: blur(4px);
  user-select: none;
}

/* Highlight snippet beneath the company name; clamped to avoid
   the cell growing too wide on large screens. */
.ds-lead-table__snippet {
  max-width: 32ch;
}

/* Row-level lock state: a locked row dims its first cell slightly. The
   lock icon and the ds-lead-contact blur carry the main differentiation. */
.ds-lead-table tbody tr.is-locked td:first-child {
  opacity: 0.85;
}
.ds-lead-table tbody tr.is-unlocked td:first-child {
  opacity: 1;
}

/* Long labels inside table badges (industry names) must not dictate the
   table's intrinsic width — clamp with an ellipsis; the full value stays
   reachable via the cell's title attribute. */
.ds-lead-table .ds-lead-table__cell--industry .ds-badge {
  display: inline-block;
  max-width: 20ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Hide less-important columns on narrower viewports. The 1600px boundary
   is content-driven: below it the 248px sidebar plus the lead table's
   intrinsic width no longer fit, so the lowest-priority columns (market,
   revenue, web signals) yield before the score and status columns clip. */
@media (max-width: 1599px) {
  .ds-lead-table .hide-lg {
    display: none;
  }
}
@media (max-width: 991px) {
  .ds-lead-table .hide-md {
    display: none;
  }
}
@media (max-width: 767px) {
  .ds-lead-table-wrap {
    overflow-x: auto;
  }
  .ds-lead-table {
    min-width: 700px;
  }
}

/* ============================================================================
 * Records View (formerly "Unlocked") — top-right export toolbar with
 * filter options (scope + format) above the records table, the success
 * banner of the last unlock, and whole-row links.
 * ========================================================================== */

.ds-records-toolbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ds-space-4);
  flex-wrap: wrap;
  padding: var(--ds-space-3) var(--ds-space-4);
  margin-bottom: 0;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-bottom: 0;
  border-radius: var(--ds-radius-md) var(--ds-radius-md) 0 0;
}

.ds-records-toolbar__count {
  display: flex;
  align-items: baseline;
  gap: var(--ds-space-2);
  padding-bottom: var(--ds-space-2);
}
.ds-records-toolbar__count-value {
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}
.ds-records-toolbar__count-label {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
}

.ds-records-toolbar__actions {
  display: flex;
  align-items: flex-end;
  gap: var(--ds-space-3);
  flex-wrap: wrap;
}

.ds-records-toolbar__field {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
  margin: 0;
}
.ds-records-toolbar__field-label {
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink-soft);
}
.ds-records-toolbar__field select {
  min-width: 150px;
}

/* When the toolbar is used, table-wrap stacks on top without top-radius. */
.ds-records-toolbar + .ds-lead-table-wrap {
  border-top: 0;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

/* Success banner after an unlock: message left, the two one-click exports of
   exactly that batch right. */
.ds-batch-banner {
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: var(--ds-space-4);
}
.ds-batch-banner__text {
  flex: 1 1 280px;
  min-width: 0;
}
.ds-batch-banner__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  margin: 0;
}

/* Whole-row link: the company link stretches over its row, so any click on
   the row opens the record while the link stays one real, named <a>. The
   selection checkbox sits above the overlay. */
.ds-lead-table--linked tbody tr {
  position: relative;
  cursor: pointer;
}
.ds-lead-table__row-link {
  color: var(--ds-ink);
  text-decoration: none;
}
.ds-lead-table__row-link::after {
  content: "";
  position: absolute;
  inset: 0;
}
.ds-lead-table__row-link:hover {
  color: var(--ds-blue);
}
.ds-lead-table__row-link:focus-visible {
  outline: none;
}
.ds-lead-table__row-link:focus-visible::after {
  box-shadow: inset 0 0 0 3px var(--ds-blue);
  border-radius: var(--ds-radius-sm);
}
.ds-lead-table--linked .ds-lead-table__row-select,
.ds-lead-table--linked .ds-badge {
  position: relative;
  z-index: 1;
}
.ds-lead-table__row-arrow {
  color: var(--ds-ink-dim);
}
.ds-lead-table--linked tbody tr:hover .ds-lead-table__row-arrow {
  color: var(--ds-blue);
}

/* ============================================================================
 * App billing — plan cards (/app/billing/plans/), Credit-Pakete
 * (/app/billing/credits/), the billing summary row and the small print.
 * The same family renders the pricing section of the home page. The
 * per-record price is the strongest argument of a pay-per-record product,
 * so every card shows it as a green pill under the credits; the
 * recommended card wears the gradient frame of .ds-card--featured.
 * ========================================================================== */
.ds-plan-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-5);
  margin-bottom: var(--ds-space-4);
}

@media (min-width: 992px) {
  .ds-plan-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: stretch;
  }
}

/* On a phone the recommended card leads, the others follow. */
@media (max-width: 991px) {
  .ds-plan-grid > .ds-card--featured {
    order: -1;
  }
}

.ds-plan-card {
  gap: var(--ds-space-2);
  padding: var(--ds-space-8) var(--ds-space-6);
  border-radius: var(--ds-radius-lg);
}

@media (max-width: 575px) {
  .ds-plan-card {
    padding: var(--ds-space-6) var(--ds-space-5);
  }
}

.ds-plan-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-2);
  flex-wrap: wrap;
}

.ds-plan-card__name {
  font-family: var(--ds-font-display);
  font-size: 1.25rem;
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
  margin: 0;
}

.ds-plan-card__audience {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
  margin: 0;
}

.ds-plan-card__price {
  margin: var(--ds-space-3) 0 0;
  font-family: var(--ds-font-display);
  font-size: clamp(2.5rem, 2.2rem + 1vw, 3rem);
  font-weight: var(--ds-fw-black);
  line-height: 1.05;
  letter-spacing: var(--ds-tracking-display);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-plan-card__unit {
  margin-left: var(--ds-space-1);
  font-family: var(--ds-font-body);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-regular);
  letter-spacing: 0;
  color: var(--ds-ink-mute);
}

.ds-plan-card__credits {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-plan-card__per-record {
  align-self: flex-start;
  white-space: nowrap;
  margin: 0;
  padding: var(--ds-space-0-5) var(--ds-space-2-5);
  border-radius: var(--ds-radius-full);
  background: var(--ds-success-tint-bg);
  color: var(--ds-success-tint-text);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
}

.ds-plan-card__cta {
  margin: var(--ds-space-4) 0 var(--ds-space-3);
}

.ds-plan-card__features {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2-5);
  margin: 0;
  padding: var(--ds-space-4) 0 0;
  border-top: 1px solid var(--ds-border-subtle);
  list-style: none;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
}

.ds-plan-card__features li {
  position: relative;
  padding-left: var(--ds-space-6);
}

.ds-plan-card__features li::before {
  content: "\F272";
  position: absolute;
  left: 0;
  top: 0;
  font-family: "bootstrap-icons";
  font-weight: var(--ds-fw-bold);
  color: var(--ds-purple);
}

.ds-plan-card__features li.is-absent {
  color: var(--ds-ink-mute);
}

.ds-plan-card__features li.is-absent::before {
  content: "\F62A";
  color: var(--ds-ink-mute);
}

/* The Free plan as one full-width line under the paid cards. */
.ds-plan-strip {
  padding: var(--ds-space-4) var(--ds-space-5);
  margin-bottom: var(--ds-space-5);
  background: var(--ds-surface);
  border: 1px dashed var(--ds-border-strong);
  border-radius: var(--ds-radius-md);
}

.ds-plan-strip__text {
  margin: 0;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
}

/* VAT, goodwill and payment notes: short left-aligned lines, not a centred
   wall of small text. */
.ds-fine-print {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  margin: var(--ds-space-4) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
}

.ds-plan-portal {
  margin: var(--ds-space-4) 0 0;
}

/* "So funktioniert ein Tarifwechsel": the proration rules behind one
   disclosure instead of a seven-line page intro. */
.ds-disclosure {
  margin-bottom: var(--ds-space-5);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
}

.ds-disclosure__summary {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  min-height: var(--ds-control-h);
  padding: 0 var(--ds-space-4);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  cursor: pointer;
  list-style: none;
}

.ds-disclosure__summary::-webkit-details-marker {
  display: none;
}

.ds-disclosure__summary::before {
  content: "\F285";
  font-family: "bootstrap-icons";
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  transition: transform var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-disclosure[open] > .ds-disclosure__summary::before {
  transform: rotate(90deg);
}

.ds-disclosure__summary:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
  border-radius: var(--ds-radius-md);
}

.ds-disclosure__body {
  padding: 0 var(--ds-space-4) var(--ds-space-4);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-prose);
  color: var(--ds-ink-soft);
}

.ds-disclosure__body p {
  margin: 0;
}

/* Credit-Pakete: credits as the headline figure, price and per-record pill
   below, the best value framed like the recommended plan. */
.ds-topup-card {
  gap: var(--ds-space-2);
  padding: var(--ds-space-5) var(--ds-space-4);
}

.ds-topup-card .ds-plan-card__per-record {
  white-space: nowrap;
  font-size: var(--ds-text-caption);
}

.ds-topup-card__badge {
  align-self: flex-start;
  white-space: normal;
}

.ds-topup-card__credits {
  margin: 0;
  font-family: var(--ds-font-display);
  letter-spacing: var(--ds-tracking-tight);
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-topup-card__unit {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-mute);
}

.ds-topup-card__price {
  margin: 0;
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-topup-card__form {
  margin-top: auto;
  padding-top: var(--ds-space-3);
}

/* Billing overview: plan, balance and next invoice side by side. */
.ds-billing-summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-4);
  margin-bottom: var(--ds-space-4);
}

@media (min-width: 768px) {
  .ds-billing-summary {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.ds-billing-delta {
  font-weight: var(--ds-fw-semi);
}

.ds-billing-delta.is-credit {
  color: var(--ds-success);
}

.ds-billing-delta.is-debit {
  color: var(--ds-ink);
}

/* ============================================================================
 * Settings page — the e-mail preferences row: a short explanation beside
 * the link to the preferences page (the sidebar no longer lists it).
 * ========================================================================== */
.ds-settings-link {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-4);
  margin-top: var(--ds-space-4);
}

.ds-settings-link__text {
  flex: 1 1 320px;
  min-width: 0;
}

.ds-settings-link__title {
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  margin: 0 0 var(--ds-space-1);
}

.ds-settings-link__desc {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
  margin: 0;
}

/* ============================================================================
 * Datensatz-Wünsche form — submit row with the quota meter beside it, and
 * the numbered "So funktioniert's" steps.
 * ========================================================================== */
.ds-wish-submit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-3) var(--ds-space-5);
  margin: var(--ds-space-2) 0 var(--ds-space-3);
}

.ds-quota-meter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  margin: 0;
}

.ds-quota-meter__item {
  display: inline-flex;
  align-items: baseline;
  gap: var(--ds-space-1-5);
  padding: var(--ds-space-1) var(--ds-space-2-5);
  background: var(--ds-surface-tint);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-full);
  font-size: var(--ds-text-caption);
}

.ds-quota-meter__item dt {
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-mute);
}

.ds-quota-meter__item dd {
  margin: 0;
  font-weight: var(--ds-fw-semi);
  font-variant-numeric: tabular-nums;
  color: var(--ds-ink);
}

.ds-steps-list {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  margin: 0;
  padding-left: var(--ds-space-5);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
}

/* ============================================================================
 * Unlock confirmation shortfall — shown when the requested amount exceeds
 * the balance: the missing credits, a "reduce" action and the Credit-Pakete
 * link. `.ds-alert` sets display:flex, so [hidden] needs its own rule.
 * ========================================================================== */
.ds-unlock-shortfall {
  flex-direction: column;
  gap: var(--ds-space-2);
}

.ds-unlock-shortfall[hidden],
.ds-unlock-shortfall [hidden] {
  display: none;
}

.ds-unlock-shortfall__text {
  margin: 0;
}

.ds-unlock-shortfall__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

/* ============================================================================
 * Stacked table (.ds-table--stack) — below 768px each row becomes a card of
 * label/value pairs fed by the cells' data-label attributes, so no column
 * (the wish status above all) hides behind a horizontal scroll.
 * ========================================================================== */
@media (max-width: 767px) {
  .ds-table-wrap:has(> .ds-table--stack) {
    border: 0;
    background: none;
    overflow: visible;
  }
  .ds-table--stack,
  .ds-table--stack tbody,
  .ds-table--stack tr,
  .ds-table--stack td {
    display: block;
    width: auto;
  }
  .ds-table--stack thead {
    display: none;
  }
  .ds-table--stack tbody tr {
    padding: var(--ds-space-3) var(--ds-space-4);
    margin-bottom: var(--ds-space-3);
    background: var(--ds-surface);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-md);
  }
  .ds-table--stack tbody tr:hover {
    background: var(--ds-surface);
  }
  .ds-table--stack tbody td {
    padding: var(--ds-space-1-5) 0;
    border: 0;
    text-align: left;
  }
  .ds-table--stack tbody td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: var(--ds-space-0-5);
    font-size: var(--ds-text-caption);
    font-weight: var(--ds-fw-medium);
    color: var(--ds-ink-mute);
  }
  .ds-table--stack tbody td:not([data-label])::before {
    content: none;
  }
  .ds-table--stack tbody td.ds-table__actions-cell {
    width: auto;
    padding-top: var(--ds-space-2);
  }
}

/* Comma-separated inline list (wish cities, further directors): the comma
   is generated, so template whitespace can never put a space before it. */
.ds-inline-list__item:not(:last-child)::after {
  content: ", ";
  white-space: pre;
}

/* Present or absent web signal on the lead sheet: a green check versus a
   grey cross, each with its own wording, so the state never rests on
   colour alone. */
.ds-presence {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1-5);
  font-size: var(--ds-text-body-sm);
  margin-right: var(--ds-space-3);
}

.ds-presence.is-present {
  color: var(--ds-ink);
}

.ds-presence.is-present .bi {
  color: var(--ds-success);
}

.ds-presence.is-absent {
  color: var(--ds-ink-mute);
}

.ds-presence.is-absent .bi {
  color: var(--ds-ink-dim);
}

/* How to reach the Free plan from a paid one, under the Free strip. */
.ds-plan-strip__how {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  margin: var(--ds-space-2) 0 0;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-mute);
}

/* ============================================================================
 * Dashboard "next step" card — the one call to action a new account sees
 * instead of several credit displays, with a three-item start checklist.
 * ========================================================================== */
.ds-next-step {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-6);
  margin-bottom: var(--ds-space-4);
  border-color: rgba(var(--ds-purple-rgb), 0.28);
  background:
    radial-gradient(
      70% 120% at 100% 0%,
      rgba(var(--ds-purple-rgb), 0.07) 0%,
      transparent 60%
    ),
    var(--ds-surface);
}

@media (min-width: 992px) {
  .ds-next-step {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    align-items: center;
  }
}

.ds-next-step__text {
  min-width: 0;
}

.ds-next-step__title {
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0 0 var(--ds-space-2);
}

.ds-next-step__lead {
  font-size: var(--ds-text-body);
  color: var(--ds-ink-soft);
  margin: 0 0 var(--ds-space-4);
}

.ds-next-step__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.ds-next-step__actions form {
  margin: 0;
}

.ds-checklist {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  margin: 0;
  padding: var(--ds-space-4);
  list-style: none;
  background: var(--ds-surface-tint);
  border-radius: var(--ds-radius-sm);
}

.ds-checklist__item {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2-5);
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink);
}

.ds-checklist__item > .bi {
  font-size: var(--ds-text-lg);
  color: var(--ds-ink-dim);
}

.ds-checklist__item.is-done {
  color: var(--ds-ink-mute);
}

.ds-checklist__item.is-done > .bi {
  color: var(--ds-success);
}

/* ============================================================================
 * Dashboard Plan-Status-Card — pricy typography, 4-column grid (icon · meta ·
 * price · credits · action), collapses to 2-column on narrow viewports.
 * ========================================================================== */
/* Block root — the card chrome comes from the surrounding ds-card; the
   root only needs to isolate its layout context. */
.ds-plan-status {
  min-width: 0;
}

.ds-plan-status__body {
  display: grid;
  grid-template-columns: auto 1fr auto auto auto;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2rem);
  padding: clamp(1.25rem, 2.5vw, 1.75rem) clamp(1.5rem, 3vw, 2rem);
}

.ds-plan-status__icon {
  width: 48px;
  height: 48px;
  border-radius: var(--ds-radius-md);
  background: var(--ds-gradient-icon-tile);
  box-shadow: var(--ds-shadow-icon-tile);
  color: var(--ds-white);
  font-size: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.ds-plan-status__meta {
  min-width: 0;
}

.ds-plan-status__eyebrow {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-mute);
  line-height: 1.2;
  margin-bottom: var(--ds-space-1);
}

.ds-plan-status__name {
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-bold);
  line-height: var(--ds-lh-tight);
  color: var(--ds-ink);
}

.ds-plan-status__renewal {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  margin-top: var(--ds-space-1);
}

.ds-plan-status__price,
.ds-plan-status__credits {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-0-5);
  padding-left: clamp(1rem, 2vw, 1.5rem);
  border-left: 1px solid var(--ds-border);
}

.ds-plan-status__price-value,
.ds-plan-status__credits-value {
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-bold);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--ds-ink);
}

.ds-plan-status__price-unit,
.ds-plan-status__credits-unit {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

.ds-plan-status__action {
  margin-left: clamp(0.5rem, 1vw, 1rem);
}

@media (max-width: 991px) {
  .ds-plan-status__body {
    grid-template-columns: auto 1fr;
    grid-template-areas:
      "icon meta"
      "price credits"
      "action action";
    row-gap: 1rem;
  }
  .ds-plan-status__icon {
    grid-area: icon;
  }
  .ds-plan-status__meta {
    grid-area: meta;
  }
  .ds-plan-status__price {
    grid-area: price;
    padding-left: 0;
    border-left: 0;
  }
  .ds-plan-status__credits {
    grid-area: credits;
    padding-left: 1rem;
  }
  .ds-plan-status__action {
    grid-area: action;
    margin-left: 0;
  }
  .ds-plan-status__action .ds-button {
    width: 100%;
  }
}

/* ============================================================================
 * App-Hero card — kept for the onboarding page (accounts/welcome.html), the
 * one app page that still opens with a hero. Every other app page uses
 * .ds-app-header. A plain white card; the hero aligns flush with
 * Bootstrap-row content (rows have a -12px gutter pull).
 * ========================================================================== */

body.ds-layout-shell--app .app-container > .ds-hero:first-child {
  margin-top: 0;
  margin-left: -12px;
  margin-right: -12px;
  margin-bottom: var(--ds-space-6);
  width: auto;
  padding: clamp(1.5rem, 2.8vw, 2.25rem) clamp(1.5rem, 3vw, 2.5rem);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  position: relative;
  overflow: hidden;
}

body.ds-layout-shell--app .app-container > .ds-hero:first-child .ds-hero__copy,
body.ds-layout-shell--app
  .app-container
  > .ds-hero:first-child
  .ds-hero__content {
  max-width: none;
  margin: 0;
  /* Prevent children (eyebrow pill, button row) from stretching to the full
       flex-column width — we want each child to be as wide as its own content. */
  align-items: flex-start;
  gap: var(--ds-space-3);
}

/* Compact credit-stat widget on the right side of the dashboard hero. */
.ds-hero-stat {
  min-width: 220px;
  padding: var(--ds-space-5) var(--ds-space-6);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  text-align: center;
}

/* ============================================================================
 * Filter-Bar (Search) — horizontal filter layer above the results
 * Row 1: query + live count + sort + apply/reset. Row 2: six group buttons
 * with anchored popover panels. Row 3: applied-filter chips. Below 768px the
 * group container becomes a full-screen sheet (accordion groups + sticky
 * apply), toggled via body.ds-filter-sheet-open by filter.js.
 * ========================================================================== */

/* Results column below the filter bar — a shrinkable layout context for
   the table wrap; spacing comes from the bar's own margins. */
.ds-search-results {
  min-width: 0;
}

/* Sticky under the app header from 768px up, so query, count and the
   group buttons stay in reach while the user scans the table. */
.ds-filter-bar {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-md);
  padding: var(--ds-space-3) var(--ds-space-4);
  margin-bottom: var(--ds-space-4);
}

@media (min-width: 768px) {
  .ds-filter-bar {
    position: sticky;
    top: calc(var(--ds-app-header-h) + var(--ds-space-2));
    z-index: 50;
    box-shadow: var(--ds-shadow-sm);
  }
}

.ds-filter-bar__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-3);
}

.ds-filter-bar__query {
  position: relative;
  flex: 1 1 240px;
  min-width: 0;
}

.ds-filter-bar__query > .bi {
  position: absolute;
  left: var(--ds-space-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--ds-ink-mute);
  pointer-events: none;
}

.ds-filter-bar__query-input {
  padding-left: var(--ds-space-10);
}

.ds-filter-bar__count {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
}

.ds-filter-count {
  font-variant-numeric: tabular-nums;
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-tight);
  color: var(--ds-ink-soft);
  white-space: nowrap;
}

.ds-filter-count strong {
  color: var(--ds-ink);
  font-weight: var(--ds-fw-bold);
  font-size: var(--ds-text-lg);
}

.ds-filter-count__balance {
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  white-space: nowrap;
}

.ds-filter-bar__sort {
  width: auto;
}

/* The mobile toggle and the sheet chrome only exist below 768px. */
.ds-filter-bar__mobile-toggle,
.ds-filter-bar__sheet-head,
.ds-filter-bar__sheet-foot {
  display: none;
}

/* Row 2 — group buttons with anchored popover panels. */
.ds-filter-bar__groups {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.ds-filter-group {
  position: relative;
}

.ds-filter-group__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  min-height: var(--ds-control-h-sm);
  padding: 0 var(--ds-space-3-5);
  font-family: inherit;
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border);
  border-radius: var(--ds-radius-full);
  cursor: pointer;
  transition:
    border-color var(--ds-dur-fast) var(--ds-ease-out),
    background-color var(--ds-dur-fast) var(--ds-ease-out),
    box-shadow var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-filter-group__toggle:hover {
  border-color: var(--ds-blue);
}

.ds-filter-group__toggle:focus-visible {
  outline: none;
  border-color: var(--ds-blue);
  box-shadow: var(--ds-shadow-focus);
}

.ds-filter-group.is-open > .ds-filter-group__toggle {
  border-color: var(--ds-blue);
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-neutral-tint-text);
}

.ds-filter-group__toggle .bi-chevron-down {
  font-size: var(--ds-text-caption);
  transition: transform var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-filter-group.is-open .bi-chevron-down {
  transform: rotate(180deg);
}

.ds-filter-group__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 var(--ds-space-1);
  border-radius: var(--ds-radius-full);
  background: var(--ds-blue);
  color: var(--ds-white);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-bold);
  line-height: 1;
}

.ds-filter-group__panel {
  position: absolute;
  top: calc(100% + var(--ds-space-2));
  left: 0;
  z-index: var(--ds-z-dropdown);
  width: min(320px, calc(100vw - var(--ds-space-6)));
  /* filter.js sets --ls-panel-shift on open when the panel would otherwise
       reach past a viewport edge; 0 is the no-JS default. */
  margin-left: var(--ls-panel-shift, 0px);
  /* filter.js sets --ls-panel-max to the viewport remainder on open so
       the panel never extends below the fold; 70vh is the no-JS fallback. */
  max-height: var(--ls-panel-max, 70vh);
  overflow-y: auto;
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-md);
  box-shadow: var(--ds-shadow-lg);
  /* No bottom padding — the sticky foot supplies it, so "Übernehmen"
       stays visible while the panel body scrolls. */
  padding: var(--ds-space-5) var(--ds-space-5) 0;
}

.ds-filter-group__panel--wide {
  width: min(640px, calc(100vw - var(--ds-space-12)));
}

/* Right-anchored variant keeps the last groups' panels inside the viewport.
   The shift then has to move the other way, so it applies as a right margin
   — a left margin on a right-anchored box pushes nothing. */
.ds-filter-group__panel--end {
  left: auto;
  right: 0;
  margin-left: 0;
  margin-right: calc(-1 * var(--ls-panel-shift, 0px));
}

.ds-filter-group__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--ds-space-5);
}

.ds-filter-group__foot {
  /* Pinned to the scrollport bottom of tall panels so the apply action
       never sits below the panel fold. */
  position: sticky;
  bottom: 0;
  display: flex;
  justify-content: flex-end;
  margin-top: var(--ds-space-4);
  padding-block: var(--ds-space-3) var(--ds-space-4);
  background: var(--ds-surface);
  border-top: 1px solid var(--ds-border-subtle);
}

.ds-filter-fieldset {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  min-width: 0;
  margin: 0 0 var(--ds-space-4);
  padding: 0;
  border: 0;
}

.ds-filter-group__grid .ds-filter-fieldset {
  margin-bottom: 0;
}

.ds-filter-fieldset__legend {
  font-size: var(--ds-text-caption);
  text-transform: none;
  letter-spacing: 0;
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  padding: 0;
  margin-bottom: var(--ds-space-1);
}

.ds-filter-fieldset__hint {
  text-transform: none;
  letter-spacing: var(--ds-tracking-base);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-mute);
}

/* Long data-driven option lists (Branche, CMS, …) scroll after ~5 rows so
   one category can never push the panel beyond the viewport. */
.ds-filter-options {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  max-height: 190px;
  overflow-y: auto;
  padding-right: var(--ds-space-1);
}

.ds-filter-range {
  display: flex;
  gap: var(--ds-space-2);
}

.ds-filter-note {
  margin: 0 0 var(--ds-space-3);
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

/* Row 3 — applied filters as removable chips; clicking re-submits the GET
   state without the removed value (server-rendered remove links). */
.ds-filter-bar__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

/* Shown by filter.js while the live count no longer matches the rendered
   table. [hidden] must win over the .ds-alert display, hence the guard. */
.ds-filter-bar__stale {
  margin: var(--ds-space-3) 0 0;
}

.ds-filter-bar__stale[hidden] {
  display: none;
}

.ds-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  padding: var(--ds-space-1) var(--ds-space-3);
  border-radius: var(--ds-radius-full);
  background: var(--ds-neutral-tint-bg);
  border: 1px solid transparent;
  color: var(--ds-neutral-tint-text);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  text-decoration: none;
  transition:
    background-color var(--ds-dur-fast) var(--ds-ease-out),
    border-color var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-filter-chip:hover {
  background: var(--ds-neutral-tint-bg);
  border-color: var(--ds-blue);
  color: var(--ds-neutral-tint-text);
}
.ds-filter-chip:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

/* Results toolbar above the table — per-page select only; count and sort
   live in the filter bar. */
.ds-results-meta {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--ds-space-2);
  margin-bottom: var(--ds-space-3);
}

.ds-results-meta__label {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
  margin: 0;
}

@media (max-width: 767px) {
  .ds-filter-bar {
    padding: var(--ds-space-4);
  }

  /* Row 1 collapses to [query | Filter (n)]; count + sort wrap below. */
  .ds-filter-bar__query {
    flex: 1 1 55%;
  }

  .ds-filter-bar__mobile-toggle {
    display: inline-flex;
    min-height: 44px;
  }

  .ds-filter-bar__apply,
  .ds-filter-bar__reset {
    display: none;
  }

  .ds-filter-bar__sort {
    flex: 1 1 auto;
    min-height: 44px;
  }

  body.ds-filter-sheet-open {
    overflow: hidden;
  }

  /* The group container becomes a full-screen sheet. */
  .ds-filter-bar__groups {
    position: fixed;
    inset: 0;
    z-index: var(--ds-z-modal);
    display: none;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0;
    background: var(--ds-surface);
    overflow-y: auto;
    padding: 0 var(--ds-space-4) var(--ds-space-4);
  }

  body.ds-filter-sheet-open .ds-filter-bar__groups {
    display: flex;
  }

  .ds-filter-bar__sheet-head {
    position: sticky;
    top: 0;
    z-index: var(--ds-z-base);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ds-space-3);
    background: var(--ds-surface);
    padding: var(--ds-space-3) 0;
    border-bottom: 1px solid var(--ds-border-subtle);
  }

  .ds-filter-bar__sheet-title {
    font-size: var(--ds-text-subsection);
    font-weight: var(--ds-fw-bold);
    color: var(--ds-ink);
  }

  .ds-filter-bar__sheet-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    border: 0;
    background: none;
    color: var(--ds-ink);
    font-size: var(--ds-text-lg);
    cursor: pointer;
    border-radius: var(--ds-radius-sm);
  }

  .ds-filter-bar__sheet-close:focus-visible {
    outline: none;
    box-shadow: var(--ds-shadow-focus);
  }

  /* Groups render as full-width accordion rows inside the sheet. */
  .ds-filter-group {
    position: static;
  }

  .ds-filter-group__toggle {
    width: 100%;
    min-height: 44px;
    border: 0;
    border-bottom: 1px solid var(--ds-border-subtle);
    border-radius: 0;
    background: none;
    padding: var(--ds-space-3) 0;
    font-size: var(--ds-text-body);
  }

  .ds-filter-group.is-open > .ds-filter-group__toggle {
    background: none;
  }

  .ds-filter-group__toggle .bi-chevron-down {
    margin-left: auto;
  }

  .ds-filter-group__panel,
  .ds-filter-group__panel--wide,
  .ds-filter-group__panel--end {
    position: static;
    width: 100%;
    /* The edge guard is a popover concern; in the sheet the panel is a
       full-width accordion pane and any leftover shift would misalign it. */
    margin-left: 0;
    margin-right: 0;
    max-height: none;
    overflow: visible;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: var(--ds-space-3) 0 var(--ds-space-4);
  }

  .ds-filter-group__grid {
    grid-template-columns: 1fr;
    gap: var(--ds-space-4);
  }

  /* Per-panel apply buttons are redundant under the sticky sheet apply. */
  .ds-filter-group__foot {
    display: none;
  }

  .ds-filter-bar__sheet-foot {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-direction: column;
    gap: var(--ds-space-2);
    margin-top: auto;
    background: var(--ds-surface);
    border-top: 1px solid var(--ds-border-subtle);
    padding: var(--ds-space-3) 0 var(--ds-space-2);
  }
}

/* === Activity-Stream === */
.activity-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.activity-list li {
  padding: 0.75rem 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px solid var(--ds-border-gray);
  font-size: var(--ds-text-body-sm);
}
.activity-list li:last-child {
  border-bottom: 0;
}
.activity-list a {
  color: var(--ds-ink);
  text-decoration: none;
  font-weight: var(--ds-fw-semi);
}
.activity-list a:hover {
  color: var(--ds-blue);
}
.activity-list .activity-time {
  color: var(--ds-ink-soft);
  font-size: 0.82rem;
  flex-shrink: 0;
}

/* Activity list row hover */
.app-shell .activity-list li {
  border-radius: var(--ds-radius-sm);
  padding: var(--ds-space-3) var(--ds-space-3-5);
  margin: 0 -14px;
  transition: background 0.2s;
}

.app-shell .activity-list li:hover {
  background: rgba(var(--ds-blue-rgb), 0.04);
}

/* Activity-list rows in the app shell: eased transitions for the
   hover state above. */

.app-shell .activity-list li {
  transition:
    transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    border-color 0.4s ease,
    background-color 0.3s ease;
  will-change: transform;
}

/* ============================================================
   Single-column app-page layout helper — centres a narrow
   content column under a header (e.g. the e-mail-preferences page).
   ============================================================ */
.app-container--narrow {
  max-width: 720px;
}

/* The app-hero pulls -12px on each side to sit flush with the Bootstrap-row
   gutter. A plain content card below it would otherwise sit 12px narrower on
   each side, so on this narrow single-column page (the only --narrow user) the
   card matches the hero's full width with the same gutter pull. */
.app-container--narrow > .ds-card {
  margin-left: -12px;
  margin-right: -12px;
}

/* Container for form submit buttons at the bottom of a card */
.ds-app-card-actions {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--ds-border-gray);
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* Lead-qualification highlight quote (unlocked_detail.html). Replaces
   the inline-styled blockquote with a tokenised rendering. */
.lead-highlight-quote {
  border-left: 3px solid var(--ds-blue);
  padding: 0.5rem 0 0.5rem 1rem;
  margin: 1rem 0;
  font-style: italic;
  color: var(--ds-ink);
  background: var(--ds-border-subtle);
  border-radius: 0 var(--ds-radius-sm) var(--ds-radius-sm) 0;
}

.ds-auth-card__heading {
  font-size: clamp(1.5rem, 1.3rem + 0.8vw, 1.875rem);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-tight);
  margin: 0 0 var(--ds-space-2);
  text-align: center;
  color: var(--ds-ink);
}

.ds-auth-card__lead {
  font-size: var(--ds-text-body);
  color: var(--ds-ink-soft);
  text-align: center;
  margin: 0 0 var(--ds-space-6);
}

/* Confirms the paid plan picked on a pricing card (?plan=). */
.ds-auth-plan-note {
  display: flex;
  gap: var(--ds-space-2);
  align-items: flex-start;
  margin: 0 0 var(--ds-space-5);
  padding: var(--ds-space-3);
  border-radius: var(--ds-radius-sm);
  background: var(--ds-neutral-tint-bg);
  color: var(--ds-neutral-tint-text);
  font-size: var(--ds-text-body-sm);
}

.ds-auth-plan-note .bi {
  margin-top: var(--ds-space-0-5);
}

/* The three Free-tariff benefits above the form on phones and tablets,
   where the aside with the same content is hidden. */
.ds-auth-benefits {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ds-space-1) var(--ds-space-4);
  margin: calc(-1 * var(--ds-space-3)) 0 var(--ds-space-5);
  padding: 0;
  list-style: none;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-soft);
}

.ds-auth-benefits li {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
}

.ds-auth-benefits li::before {
  content: "\F633";
  font-family: "bootstrap-icons";
  color: var(--ds-success);
}

@media (min-width: 992px) {
  .ds-auth-benefits {
    display: none;
  }
}

/* One reassuring line directly under the submit button. */
.ds-auth-card__micro {
  margin: var(--ds-space-2) 0 0;
  text-align: center;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

/* Auth-card footer strip (divider + caption text + optional link). */
.ds-auth-card__footer {
  margin-top: var(--ds-space-5);
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-border-subtle);
  text-align: center;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-soft);
}

/* Inline anchor inside .ds-auth-card__footer and forgot-password row. */
.ds-auth-card__link {
  color: var(--ds-blue);
  font-weight: var(--ds-fw-semi);
}

/* Forgot-password / secondary-action row above the footer divider. */
.ds-auth-card__action-hint {
  margin-top: var(--ds-space-4);
  text-align: center;
  font-size: var(--ds-text-caption);
}

/* Centered icon tile used on status/confirmation cards (64 × 64 px). */
.ds-auth-card__icon {
  width: 64px;
  height: 64px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--ds-radius-lg);
  font-size: 28px;
  margin: 0 auto var(--ds-space-5);
}

/* Colour modifiers for .ds-auth-card__icon: the brand tile is the
   gradient icon tile, the status tiles keep their tint pairs. */
.ds-auth-card__icon--brand {
  background: var(--ds-gradient-icon-tile);
  color: var(--ds-white);
  box-shadow: var(--ds-shadow-icon-tile-lg);
}

.ds-auth-card__icon--danger {
  background: var(--ds-danger-tint);
  color: var(--ds-danger);
}

.ds-auth-card__icon--success {
  background: var(--ds-success-tint);
  color: var(--ds-success);
}

/* Modifier that centres all direct children inside a ds-auth-card. */
.ds-auth-card--centered {
  text-align: center;
}

/* The dark brand panel's title, benefit list and footnote. */
.ds-auth-aside__title {
  margin: 0;
  font-size: clamp(1.5rem, 1.3rem + 0.8vw, 2rem);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink-on-dark-strong);
}

.ds-auth-card__aside-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-4);
  font-size: var(--ds-text-body);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-on-dark-body);
}

.ds-auth-card__aside-list li {
  display: flex;
  gap: var(--ds-space-3-5);
  align-items: flex-start;
}

.ds-auth-card__aside-list .ds-icon-tile {
  margin-top: var(--ds-space-0-5);
}

.ds-auth-card__footnote {
  margin: 0;
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-surface-dark-border);
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-on-dark-muted);
}

/* Token-driven text-colour utilities (brand ramp for status/muted text). */
.ds-text-success {
  color: var(--ds-success);
}

.ds-text-brand {
  color: var(--ds-blue);
}

.ds-text-sm {
  font-size: var(--ds-text-body-sm);
}

/* Token-clean pagination component (replaces Bootstrap .pagination defaults). */
.ds-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-6);
  flex-wrap: wrap;
}

.ds-pagination__link {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-2) var(--ds-space-4);
  background: var(--ds-surface);
  border: 1px solid var(--ds-border-subtle);
  border-radius: var(--ds-radius-sm);
  color: var(--ds-ink);
  text-decoration: none;
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-semi);
  transition:
    border-color var(--ds-dur-fast) var(--ds-ease-out),
    color var(--ds-dur-fast) var(--ds-ease-out),
    background var(--ds-dur-fast) var(--ds-ease-out);
}

.ds-pagination__link:hover,
.ds-pagination__link:focus-visible {
  border-color: var(--ds-blue);
  color: var(--ds-blue);
}

.ds-pagination__link--disabled {
  color: var(--ds-ink-mute);
  background: var(--ds-surface-tint);
  cursor: not-allowed;
}

.ds-pagination__link--disabled:hover {
  border-color: var(--ds-border-subtle);
  color: var(--ds-ink-mute);
}

/* Current page in numbered pagination rows (shown on the /design/
   reference page; the blog index uses prev/status/next only). */
.ds-pagination__link--active {
  background: var(--ds-blue);
  border-color: var(--ds-blue);
  color: var(--ds-white);
  pointer-events: none;
}

.ds-pagination__status {
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink-soft);
  font-weight: var(--ds-fw-semi);
  padding: 0 var(--ds-space-2);
}

/* Phones: tighter gaps and no wrapping so prev/status/next stay on one row
   ("Weiter" used to orphan onto a second line at 390px). The 44px link
   height comes from the shared mobile touch-target block. */
@media (max-width: 575px) {
  .ds-pagination {
    flex-wrap: nowrap;
    gap: var(--ds-space-2);
  }
  .ds-pagination__link {
    padding-inline: var(--ds-space-3);
  }
  .ds-pagination__status {
    white-space: nowrap;
  }
}

/* Token-clean inline spinner (replaces Bootstrap .spinner-border).
   Pairs with badges, buttons, and any inline status surface. */
.ds-spinner {
  display: inline-block;
  width: 0.95em;
  height: 0.95em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -0.1em;
  animation: spin 0.7s linear infinite;
}

.ds-spinner--sm {
  width: 0.8em;
  height: 0.8em;
  border-width: 1.5px;
}
.ds-spinner--lg {
  width: 1.4em;
  height: 1.4em;
  border-width: 2px;
}

/* The lead-detail work sheet (unlock/unlocked_detail.html): a full-width
   contact card with copy buttons, then "Einordnung" and "Herkunft" side by
   side from 992px. Rows are label/value pairs in a definition list. */
.ds-sheet-card {
  margin-bottom: var(--ds-space-4);
  gap: var(--ds-space-4);
}

.ds-sheet-card__title {
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  margin: 0;
}

.ds-sheet-card__subtitle {
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-semi);
  color: var(--ds-ink);
  margin: var(--ds-space-2) 0 0;
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-border-subtle);
}

.ds-sheet-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 var(--ds-space-4);
  align-items: start;
}

@media (min-width: 992px) {
  .ds-sheet-grid {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  }
}

.ds-sheet-list {
  display: flex;
  flex-direction: column;
  margin: 0;
}

/* The contact card runs its rows in two columns on wide screens; a value
   and its copy button stay on one line. */
@media (min-width: 992px) {
  .ds-sheet-list--contact {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--ds-space-8);
  }
}

.ds-sheet-list--contact .ds-sheet-row {
  grid-template-columns: 150px minmax(0, 1fr);
}

.ds-sheet-list--contact .ds-sheet-row__value {
  flex-wrap: nowrap;
}

@media (max-width: 575px) {
  .ds-sheet-list--contact .ds-sheet-row {
    grid-template-columns: minmax(0, 1fr);
  }
}

.ds-sheet-row {
  display: grid;
  grid-template-columns: minmax(140px, 35%) minmax(0, 1fr);
  gap: var(--ds-space-3);
  align-items: center;
  padding: var(--ds-space-2-5) 0;
  border-bottom: 1px solid var(--ds-border-subtle);
  min-height: 48px;
}

.ds-sheet-list > .ds-sheet-row:last-child {
  border-bottom: 0;
}

/* Long values (reasons, meta texts) read better with the label above. */
.ds-sheet-list--stacked .ds-sheet-row {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ds-space-1);
}

.ds-sheet-row__label {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-mute);
  margin: 0;
}

.ds-sheet-row__label .bi {
  color: var(--ds-ink-mute);
}

.ds-sheet-row__value {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  min-width: 0;
  margin: 0;
  font-size: var(--ds-text-body-sm);
  color: var(--ds-ink);
  overflow-wrap: anywhere;
}

@media (max-width: 575px) {
  .ds-sheet-row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ds-space-1);
  }
}

/* Small icon button that copies one value; the check icon confirms. */
.ds-copy-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ds-control-h-sm);
  height: var(--ds-control-h-sm);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--ds-radius-xs);
  background: transparent;
  color: var(--ds-ink-mute);
  cursor: pointer;
  transition:
    background-color var(--ds-dur-fast) var(--ds-ease-standard),
    color var(--ds-dur-fast) var(--ds-ease-standard);
}

.ds-copy-button:hover {
  background: var(--ds-surface-tint);
  border-color: var(--ds-border);
  color: var(--ds-ink);
}

.ds-copy-button:focus-visible {
  outline: none;
  box-shadow: var(--ds-shadow-focus);
}

.ds-copy-button.is-done {
  color: var(--ds-success);
}

/* Three scores, each with its one-line meaning. */
.ds-sheet-scores {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--ds-space-3);
  margin: 0;
}

.ds-sheet-score {
  padding: var(--ds-space-3) var(--ds-space-4);
  background: var(--ds-surface-tint);
  border-radius: var(--ds-radius-sm);
}

.ds-sheet-score__label {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-soft);
}

.ds-sheet-score__value {
  margin: var(--ds-space-1) 0 0;
  font-family: var(--ds-font-display);
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-bold);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  color: var(--ds-ink);
}

.ds-sheet-score__suffix {
  font-size: var(--ds-text-body-sm);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-ink-mute);
}

.ds-sheet-score__hint {
  margin: var(--ds-space-1) 0 0;
  font-size: var(--ds-text-caption);
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
}

/* Highlight quote — the qualification's one-sentence summary. */
.ds-highlight-quote {
  margin: 0;
  padding: var(--ds-space-3) var(--ds-space-4);
  background: var(--ds-surface-tint);
  border-left: 3px solid var(--ds-blue);
  border-radius: 0 var(--ds-radius-sm) var(--ds-radius-sm) 0;
  font-size: var(--ds-text-body);
  color: var(--ds-ink-soft);
  line-height: var(--ds-lh-base);
}

/* Tech-stack and offering badge row. */
.ds-detail-techstack {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

/* Profile prose block. */
.ds-detail-prose {
  margin: 0;
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-prose);
}

/* Expiry line under a ready export's status badge. */
.ds-export-expiry {
  display: block;
  margin-top: var(--ds-space-1);
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  white-space: nowrap;
}

/* === A11y-Post-Migration Token-Typography Fix === */

/* Inline <code> within prose / section-leads — ensures WCAG AA on light surfaces.
   Bootstrap default is pink #d63384 which fails 4.5:1 on --ds-surface-tint.
   Excludes eyebrow-pills (already 11px — 0.92em would drop to 10.12px). */
.ds-section__lead code,
.ds-prose code,
.ds-section code:not(pre code):not(.ds-eyebrow code) {
  color: var(--ds-ink);
  background: rgba(var(--ds-blue-rgb), 0.08);
  padding: 1px 6px;
  border-radius: var(--ds-radius-xs);
  font-size: 0.86em;
  font-family: var(--ds-font-mono);
  font-weight: var(--ds-fw-medium);
}

/* The same inline code on the dark stage: lavender on a faint glass. */
.ds-page-hero code,
.ds-cta-band code,
.ds-section--dark code:not(pre code):not(.ds-eyebrow code) {
  color: var(--ds-lavender-light);
  background: var(--ds-surface-glass-strong);
  padding: 1px 6px;
  border-radius: var(--ds-radius-xs);
  font-size: 0.92em;
  font-family: var(--ds-font-mono);
}

.ds-eyebrow code {
  font-size: inherit;
  font-family: var(--ds-font-mono);
  background: transparent;
  padding: 0;
  color: inherit;
  font-weight: inherit;
}

/* Standalone links inside text-blocks — explicit underline + color for distinction
   from surrounding text (WCAG 1.4.1 non-color-dependent). */
.ds-section__lead a:not(.ds-button):not(.ds-badge),
.ds-prose a:not(.ds-button):not(.ds-badge),
p a:not(.ds-button):not(.ds-badge) {
  color: var(--ds-blue);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: var(--ds-fw-semi);
}
.ds-section__lead a:not(.ds-button):not(.ds-badge):hover,
.ds-prose a:not(.ds-button):not(.ds-badge):hover,
p a:not(.ds-button):not(.ds-badge):hover {
  color: var(--ds-blue-dark);
}

/* ============================================================================
 * Table scroll affordance — shared pattern for every horizontal
 * table scroller. Two signals: (1) an always-styled horizontal scrollbar,
 * (2) scroll shadows via the background-attachment trick — the inner edge
 * shadow only shows while content is actually cut off, because the local-
 * attached cover layer slides over it at the scroll ends.
 * ========================================================================== */

.ds-table-wrap,
.ds-lead-table-wrap {
  /* Containing block for visually-hidden cell content: without it those
     absolutely positioned spans escape the scroller and widen the page. */
  position: relative;
  background:
    linear-gradient(90deg, var(--ds-surface) 30%, rgba(var(--ds-white-rgb), 0))
      left center / 48px 100% no-repeat local,
    linear-gradient(270deg, var(--ds-surface) 30%, rgba(var(--ds-white-rgb), 0))
      right center / 48px 100% no-repeat local,
    radial-gradient(
        farthest-side at 0 50%,
        rgba(var(--ds-ink-rgb), 0.35),
        transparent
      )
      left center / 22px 100% no-repeat scroll,
    radial-gradient(
        farthest-side at 100% 50%,
        rgba(var(--ds-ink-rgb), 0.35),
        transparent
      )
      right center / 22px 100% no-repeat scroll var(--ds-surface);
  scrollbar-width: thin;
  scrollbar-color: var(--ds-surface-4) var(--ds-surface-tint);
}

.ds-table-wrap::-webkit-scrollbar,
.ds-lead-table-wrap::-webkit-scrollbar {
  height: 8px;
}

.ds-table-wrap::-webkit-scrollbar-track,
.ds-lead-table-wrap::-webkit-scrollbar-track {
  background: var(--ds-surface-tint);
  border-radius: var(--ds-radius-full);
}

.ds-table-wrap::-webkit-scrollbar-thumb,
.ds-lead-table-wrap::-webkit-scrollbar-thumb {
  background: var(--ds-surface-4);
  border-radius: var(--ds-radius-full);
}

/* A ds-compare-table inside a scroll wrap brings its own border; the wrap
   recedes to a pure scroll container in that case (reference-page demos). */
.ds-table-wrap:has(> .ds-compare-table) {
  border: none;
  box-shadow: none;
}

/* Code demos share the always-visible scrollbar affordance. */
pre {
  scrollbar-width: thin;
  scrollbar-color: var(--ds-surface-4) transparent;
}

pre::-webkit-scrollbar {
  height: 8px;
}

pre::-webkit-scrollbar-thumb {
  background: var(--ds-surface-4);
  border-radius: var(--ds-radius-full);
}

/* ============================================================================
 * Lead-Table card collapse — opt-in via the
 * .ds-lead-table--cards + .ds-lead-table-wrap--cards modifiers. Below 768px
 * each row renders as a ds-card--lead-style stacked card (company name,
 * location/industry/employees, contact icons, score pills, action) instead
 * of a 700px horizontal-scroll table. The per-cell element classes are set
 * in templates/leads/search.html; tables without the modifier keep the
 * scroll-wrapper strategy.
 * ========================================================================== */

@media (max-width: 767px) {
  .ds-lead-table-wrap--cards {
    overflow: visible;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
  }

  .ds-lead-table--cards {
    display: block;
    min-width: 0;
  }

  .ds-lead-table--cards thead {
    display: none;
  }

  .ds-lead-table--cards tbody {
    display: flex;
    flex-direction: column;
    gap: var(--ds-space-3);
  }

  .ds-lead-table--cards tbody tr {
    display: block;
    position: relative;
    background: var(--ds-surface);
    border: 1px solid var(--ds-border-subtle);
    border-radius: var(--ds-radius-md);
    box-shadow: var(--ds-shadow-sm);
    padding: var(--ds-space-4);
  }

  .ds-lead-table--cards tbody tr:hover {
    background: var(--ds-surface);
  }

  .ds-lead-table--cards td {
    display: inline-block;
    border: 0;
    padding: 0;
    vertical-align: middle;
  }

  /* Card header: company name spans the row; the action button floats
       top-right, so the cell reserves space for it. */
  .ds-lead-table--cards td.ds-lead-table__cell--company {
    display: block;
    padding-right: var(--ds-space-12);
    margin-bottom: var(--ds-space-2);
  }

  /* Long unbroken company names must wrap inside the narrow card instead of
       overflowing it; flex-start keeps the icon aligned to the first line. */
  .ds-lead-table--cards .ds-lead-table__company {
    min-width: 0;
    overflow-wrap: anywhere;
    align-items: flex-start;
  }

  .ds-lead-table--cards td.ds-lead-table__cell--location,
  .ds-lead-table--cards td.ds-lead-table__cell--industry {
    margin: 0 var(--ds-space-3) var(--ds-space-2) 0;
  }

  /* Size/employees is table-hidden via hide-md at this width, but the
       card spec includes it — re-show inside the card. */
  .ds-lead-table--cards td.ds-lead-table__cell--size {
    display: inline-block;
    margin: 0 var(--ds-space-3) var(--ds-space-2) 0;
  }

  /* Secondary columns stay hidden in card mode. */
  .ds-lead-table--cards td.ds-lead-table__cell--market,
  .ds-lead-table--cards td.ds-lead-table__cell--revenue,
  .ds-lead-table--cards td.ds-lead-table__cell--web {
    display: none;
  }

  .ds-lead-table--cards td.ds-lead-table__cell--contact {
    display: inline-block;
    margin: 0 var(--ds-space-4) var(--ds-space-1) 0;
  }

  .ds-lead-table--cards td.ds-lead-table__cell--score {
    display: inline-block;
    margin-bottom: var(--ds-space-1);
  }

  .ds-lead-table--cards td.ds-lead-table__cell--action {
    position: absolute;
    top: var(--ds-space-3);
    right: var(--ds-space-3);
    padding: 0;
  }

  /* The single-unlock button is too wide for the top-right corner, where it
       would cover the company name; it closes the card at full width. */
  .ds-lead-table--cards td.ds-lead-table__cell--unlock {
    position: static;
    display: block;
    margin-top: var(--ds-space-3);
  }

  .ds-lead-table--cards .ds-lead-table__unlock {
    width: 100%;
  }

  /* Row-selection checkbox (unlocked list): pinned top-left; the company
       header cell indents to clear it. */
  .ds-lead-table--cards td.ds-lead-table__row-select {
    position: absolute;
    top: var(--ds-space-4);
    left: var(--ds-space-4);
    width: auto;
    padding: 0;
  }

  .ds-lead-table--cards
    td.ds-lead-table__row-select
    ~ td.ds-lead-table__cell--company {
    padding-left: var(--ds-space-10);
  }
}

/* ============================================================================
 * Mobile floor & touch targets — 44px interactive floor < 768px.
 * Grouped here so every undersized control shares one documented fix.
 * ========================================================================== */

@media (max-width: 767px) {
  /* The toast overlays the whole site header on a phone, so the control that
     dismisses it has to meet the same 44px floor as every other close button
     — it was the one control still sized 28px. */
  .ls-toast-close {
    min-width: 44px;
    min-height: 44px;
  }

  /* .ds-form-help carries the only explanation on several screens (the wish
     status, the export failure reason, the consent error). At caption size it
     was the smallest text on the page precisely where it mattered most. */
  .ds-form-help {
    font-size: var(--ds-text-body-sm);
  }

  /* Every button gains the 44px hit box. The floor used to be applied to
       --sm only, which left the base .ds-button at 35px (9px padding + a
       1.2 line-height on the 14px body-sm scale) — so the compact variant
       rendered TALLER than the primary and secondary actions it is meant
       to de-emphasise. The type scale and padding are untouched; only the
       hit area grows. */
  .ds-button {
    min-height: 44px;
  }

  /* Icon-only compact buttons (table row actions, chip removers) satisfy the
       height floor but stay 36px wide. A square target needs the floor on
       both axes; buttons carrying a text label are already wider. */
  .ds-button--sm:not(:has(> :not(i):not(svg))) {
    min-width: 44px;
  }

  /* Header brand: the header is 80px tall, only the anchor box was
       short — extend its own hit area without moving the layout. */
  .ds-site-header__brand {
    min-height: 44px;
  }

  /* The two app-header controls next to the burger. Both are permanent
       fixtures of the logged-in shell and both sat under the floor (badge
       32px, avatar 36px); the header has the vertical room, so only the
       hit area grows. */
  .ds-wallet-badge {
    min-height: 44px;
  }

  .ds-avatar-menu {
    width: 44px;
    height: 44px;
  }

  /* Mobile-nav burger: the primary mobile menu/drawer trigger — raise its
       32px glyph box to the 44px touch floor (the 20px SVG stays centered). */
  .ds-site-header__burger {
    width: 44px;
    height: 44px;
  }

  /* Footer link rows: minimal app footer, marketing footer columns and
       the legal strip (incl. the consent re-open button). */
  .ds-app-footer__links a,
  .ds-app-footer__links .ds-footer-link-button,
  .ds-site-footer__legal-links a,
  .ds-site-footer__legal-links .ds-footer-link-button {
    min-height: 44px;
    min-width: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .ds-site-footer__col a {
    min-height: 44px;
  }

  /* Pagination links (blog index, search, unlocked list). Numbered page
       links are narrow, so the floor applies to both axes. */
  .ds-pagination__link {
    min-height: 44px;
    min-width: 44px;
    justify-content: center;
  }

  /* Auth-card secondary links ("Zurück zur Anmeldung", "Passwort
       vergessen?") render as bare 15px text otherwise. */
  .ds-auth-card__link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Inline links inside check labels (consent checkboxes) deliberately carry
       NO vertical padding — for the same reason the consent banner's privacy
       link never did. Vertical padding on an inline element does not grow its
       line box, so a 46px hit area inside a 21px line overlaps the next line
       by 25px. On the signup label ("… die AGB und die Datenschutzerklärung.")
       the two links land on consecutive lines, the later one paints on top,
       and "AGB" loses its lower half including its own centre point: aiming at
       the terms opened the privacy policy. A 44px target is unreachable here
       by construction — clearing it would need a line-height of 46px on a
       consent row — and both links sit in a running sentence, which WCAG 2.2
       SC 2.5.8 exempts from the target-size floor. */

  /* The dashboard's "Weiter einrichten" continuation link: a standalone call
       to action at the caption scale, not an inline link inside a sentence,
       so the floor applies. Negative block margin keeps the surrounding
       baseline row at its current height — same idiom as .ds-card__header a
       below. */
  /* Also covers the class-less direct-child links (the plans page's
     "Details" measured 46x21). `.ds-alert` is a flex container, so a direct
     child is a flex item and not a link inside running text — the floor can
     apply here without the line-overlap that rules it out for inline links. */
  .ds-alert a.small,
  .ds-alert > a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-block: -12px;
  }

  /* Unlocked-detail payoff links (Website / E-Mail / Telefon). */
  .ds-sheet-list--contact .ds-sheet-row__value a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Blog category chips are explicitly named by the contract's 44px rule. */
  .cluster-chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: var(--ds-space-2-5) var(--ds-space-4);
  }

  /* Card-header inline actions ("Alle anzeigen") and the recent-unlock
       list rows on the dashboard. */
  .ds-card__header a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-block: -10px;
  }

  .activity-list a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Breadcrumb links (blog articles) — negative margins keep the visual
       rhythm while the hit box meets the 44px floor in both axes. The wider
       column gap gives back the 4px each side the inline margin takes, so a
       link wider than 44px does not touch its separator. */
  .ds-breadcrumb {
    column-gap: var(--ds-space-2-5);
  }
  .ds-breadcrumb a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    margin-block: -12px;
    margin-inline: -4px;
  }

  /* Company-name links inside lead/record tables. */
  .ds-lead-table td a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-block: -11px;
  }

  /* Inline prose links (mail addresses, body links): vertical tap padding
       extends the hit box without moving the inline line box. */
  p a:not(.ds-button):not(.ds-badge),
  .ds-prose a:not(.ds-button):not(.ds-badge) {
    padding-block: 12px;
  }

  /* Row-select, filter, consent and preference checkboxes: enlarge the
       visible box; the surrounding label/row carries the rest of the hit
       area. */
  .ds-lead-table__row-select input[type="checkbox"],
  .form-check-input {
    width: 24px;
    height: 24px;
  }

  /* Checkbox/radio inputs additionally get an invisible hit-area
       extension around the 24px visual box (-10px on each side = 44px
       effective target) — covers the label-less row-select variant where
       no text area widens the wrapping ds-check label. */
  .ds-check__input {
    position: relative;
  }

  .ds-check__input::after {
    content: "";
    position: absolute;
    inset: -10px;
  }

  /* Applied-filter remove chips are named explicitly by the contract's
       44px rule. */
  .ds-filter-chip {
    min-height: 44px;
  }

  /* Sticky sheet footer of the mobile filter sheet (apply + reset). */
  .ds-filter-bar__sheet-foot .ds-button {
    min-height: 44px;
  }

  /* Badge-shaped links (e.g. reference-page TOC chips): invisible
       vertical hit-area extension that leaves the visual size untouched. */
  a.ds-badge {
    position: relative;
  }

  a.ds-badge::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
  }

  /* Sticky-CTA dismiss control. */
  .ds-sticky-cta-close {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Delete-account modal dismiss control (includes/_modal_delete_account). */
  .ds-modal__close {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Data-table cells tighten so the first scroll viewport ends near a
       column boundary instead of mid-element. */
  .ds-table thead th,
  .ds-table tbody td {
    padding: var(--ds-space-3);
  }

  /* Row actions inside scrolling data tables go icon-only — the full
       label would be cut mid-button at the wrap edge; the accessible name
       stays on the control via aria-label/title. */
  .ds-table__action-label {
    display: none;
  }

  .ds-table__actions-cell .ds-button {
    min-width: 44px;
    justify-content: center;
  }
}

/* ds-modal — minimal overlay component used for destructive-action confirms.
   Driven by static/js/modals.js (data-ds-modal-open/close attributes). */
.ds-modal[hidden] {
  display: none;
}
.ds-modal {
  position: fixed;
  inset: 0;
  z-index: var(--ds-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--ds-space-6);
}
.ds-modal__overlay {
  position: absolute;
  inset: 0;
  background: rgba(var(--ds-ink-rgb), 0.55);
  backdrop-filter: blur(2px);
  cursor: pointer;
}
.ds-modal__panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 520px;
  /* The container is fixed/inset-0 with 24px padding, so the panel has to
     measure against the *visible* viewport. 100vh is the large viewport on
     mobile, which would let the panel outgrow its centred container and clip
     the close and confirm controls off both ends. */
  max-height: calc(100svh - 48px);
  overflow-y: auto;
  background: var(--ds-white);
  border-radius: var(--ds-radius-sm);
  box-shadow: var(--ds-shadow-modal);
  padding: var(--ds-space-6);
}

@supports not (max-height: 100svh) {
  .ds-modal__panel {
    max-height: calc(100vh - 48px);
  }
}

.ds-modal__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--ds-space-4);
  margin-bottom: var(--ds-space-4);
}
.ds-modal__title {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  margin: 0;
  color: var(--ds-ink);
}
.ds-modal__close {
  background: transparent;
  border: 0;
  font-size: 24px;
  line-height: 1;
  color: var(--ds-ink);
  cursor: pointer;
  padding: var(--ds-space-1);
  border-radius: var(--ds-radius-xs);
  transition:
    background var(--ds-dur-fast) var(--ds-ease-out),
    color var(--ds-dur-fast) var(--ds-ease-out);
}
.ds-modal__close:hover,
.ds-modal__close:focus-visible {
  background: rgba(var(--ds-ink-rgb), 0.08);
}
.ds-modal__body {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}
.ds-modal__footer {
  display: flex;
  justify-content: flex-end;
  /* At 320px the two actions of the delete-account dialog share 212px, which
     leaves "Abbrechen" 97px for a 105px word — clipped, since .ds-button hides
     its overflow for the hover sheen. Wrapping gives each action the full row
     instead of cutting the label; above ~360px both still fit side by side. */
  flex-wrap: wrap;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-5);
  padding-top: var(--ds-space-3);
  border-top: 1px solid var(--ds-surface-3);
}
body.ds-modal-open {
  overflow: hidden;
} /* ============================================================
   ds-progress — token-driven progress bar component.
   Replaces raw Bootstrap .progress / .progress-bar classes
   in app-shell templates (e.g. onboarding completion tracker).
   The track height is baked into the component; the fill width
   is set via an inline style="width: X%" on .ds-progress__bar
   because it is driven by a server-computed percentage value.
   ============================================================ */

/* ============================================================
   ds-app-card-footnote — muted caption line below a form card.
   Renders privacy / label references at reduced prominence.
   ============================================================ */
.ds-app-card-footnote {
  margin-top: var(--ds-space-4);
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
  line-height: 1.55;
} /* ============================================================
   ds-orb position/size modifiers — replaces per-instance
   inline styles on decorative background orbs.
   Each modifier encodes the section and colour so the values
   stay maintainable and the HTML stays attribute-free.
   ============================================================ */
/* ============================================================
   ds-hero__lead--secondary — secondary lead paragraph variant.
   Slightly smaller and softer than the primary lead paragraph
   to create visual hierarchy without a separate component.
   No --ds-* token exists for this specific 0.95 em step-down;
   the value is intentionally close-to-1em and maintenance-safe.
   ============================================================ */
/* ============================================================
   ul.ds-feature-grid — list-reset when ds-feature-grid is
   applied to a <ul> element.  Keeps the component stylesheet
   correct regardless of the host element semantics.
   ============================================================ */

/* ============================================================================
 * ds-demo-* — /design/ reference-page showcase scaffolding
 * Page-internal utilities used ONLY by templates/core/design_system.html to
 * lay out component demos without inline styles. Token-composed; never use
 * these classes in product templates — they document nothing and may change
 * with the reference page.
 * ========================================================================== */

/* Left-aligned section headers (the reference page reads as a document). */
.ds-demo-title {
  text-align: left;
}
.ds-demo-lead,
.ds-section__inner .ds-demo-lead {
  text-align: left;
  margin-left: 0;
}

/* Label above a demo block: the eyebrow type without the pill, so the
   reference page keeps its pills for the section openers. */
.ds-demo-label {
  display: block;
  margin-bottom: var(--ds-space-3);
}
.ds-eyebrow.ds-demo-label {
  display: flex;
  width: fit-content;
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
}
.ds-eyebrow.ds-demo-label::before {
  display: none;
}

/* Generic demo layout: wrapping flex row and vertical stack. */
.ds-demo-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-3);
  align-items: center;
}
.ds-demo-stack {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-6);
}
.ds-demo-stack--sm {
  gap: var(--ds-space-3);
}
.ds-demo-max-sm {
  max-width: 360px;
}

/* Dark code sample block under each demo. */
.ds-demo-code {
  margin-top: var(--ds-space-8);
  padding: var(--ds-space-5);
  background: var(--ds-surface-dark);
  color: rgba(var(--ds-white-rgb), 0.92);
  border-radius: var(--ds-radius-md);
  overflow-x: auto;
  font-family: var(--ds-font-mono);
  font-size: var(--ds-text-body-sm);
}
.ds-demo-caption {
  margin: 0;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}
.ds-demo-note {
  margin: var(--ds-space-2) 0 0;
  font-size: var(--ds-text-caption);
  color: var(--ds-ink-mute);
}

/* Demo card copy (titles/body text inside bare .ds-card demos). */
.ds-demo-card-title {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-ink);
  margin: 0;
}
.ds-demo-card-title--sm {
  font-size: var(--ds-text-body);
}
.ds-demo-card-text {
  margin: 0;
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
}
.ds-demo-list {
  margin: 0;
  padding-left: var(--ds-space-5);
  color: var(--ds-ink-soft);
  font-size: var(--ds-text-body-sm);
  line-height: var(--ds-lh-prose);
}

/* Color swatches (palette section) + token chips (token reference). */
.ds-demo-swatch {
  height: 100px;
  border-radius: var(--ds-radius-md);
}
.ds-demo-swatch--short {
  height: 72px;
}
.ds-demo-swatch--tile {
  height: 60px;
  border-radius: var(--ds-radius-sm);
}
.ds-demo-swatch--bordered {
  border: 1px solid var(--ds-border);
}
.ds-demo-swatch-caption {
  padding-top: var(--ds-space-2);
}

.ds-demo-bg--blue {
  background: var(--ds-blue);
}
.ds-demo-bg--red {
  background: var(--ds-red);
}
.ds-demo-bg--purple {
  background: var(--ds-purple);
}
.ds-demo-bg--ink {
  background: var(--ds-ink);
}
.ds-demo-bg--ink-soft {
  background: var(--ds-ink-soft);
}
.ds-demo-bg--ink-mute {
  background: var(--ds-ink-mute);
}
.ds-demo-bg--surface {
  background: var(--ds-surface);
}
.ds-demo-bg--surface-tint {
  background: var(--ds-surface-tint);
}
.ds-demo-bg--surface-muted {
  background: var(--ds-surface-muted);
}
.ds-demo-bg--surface-dark {
  background: var(--ds-surface-dark);
}
.ds-demo-bg--surface-dark-2 {
  background: var(--ds-surface-dark-2);
}
.ds-demo-bg--success {
  background: var(--ds-success);
}
.ds-demo-bg--success-bright {
  background: var(--ds-success-bright);
}
.ds-demo-bg--warn {
  background: var(--ds-warn);
}
.ds-demo-bg--danger {
  background: var(--ds-danger);
}
.ds-demo-bg--info {
  background: var(--ds-info);
}
.ds-demo-bg--blue-dark {
  background: var(--ds-blue-dark);
}
.ds-demo-bg--blue-deep {
  background: var(--ds-blue-deep);
}
.ds-demo-bg--accent-text {
  background: var(--ds-accent-text);
}
.ds-demo-bg--accent-tint {
  background: var(--ds-accent-tint-bg);
}
.ds-demo-bg--neutral-tint {
  background: var(--ds-neutral-tint-bg);
}
.ds-demo-bg--ink-dim {
  background: var(--ds-ink-dim);
}
.ds-demo-bg--border {
  background: var(--ds-border);
}
.ds-demo-bg--border-control {
  background: var(--ds-border-control);
}
.ds-demo-bg--on-dark-accent {
  background: var(--ds-ink-on-dark-accent);
}
.ds-demo-bg--success-tint-bg {
  background: var(--ds-success-tint-bg);
}
.ds-demo-bg--warn-tint-bg {
  background: var(--ds-warn-tint-bg);
}
.ds-demo-bg--danger-tint-bg {
  background: var(--ds-danger-tint-bg);
}
.ds-demo-bg--purple-dark {
  background: var(--ds-purple-dark);
}
.ds-demo-bg--red-deep {
  background: var(--ds-red-deep);
}
.ds-demo-bg--violet-tint {
  background: var(--ds-violet-tint-bg);
}
.ds-demo-bg--surface-dark-violet {
  background: var(--ds-surface-dark-violet);
}
.ds-demo-bg--gradient-brand {
  background: var(--ds-gradient-brand);
}
.ds-demo-bg--gradient-cta {
  background: var(--ds-gradient-cta);
}
.ds-demo-bg--gradient-meter {
  background: var(--ds-gradient-meter);
}
.ds-demo-bg--mesh-dark {
  background: var(--ds-gradient-mesh-dark);
}
.ds-demo-bg--mesh-light {
  background: var(--ds-gradient-mesh-light);
}
.ds-demo-bg--grid-dark {
  background-color: var(--ds-surface-dark-2);
  background-image: var(--ds-pattern-grid-dark);
  background-size: 24px 24px;
}

/* A swatch that carries a type sample, centred. */
.ds-demo-swatch--text {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--ds-space-4);
}

/* A dark stage panel for on-dark demos: any of the dark surfaces
   (.ds-section--dark, .ds-home-hero) at panel size, with the grid. */
.ds-demo-stage {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: var(--ds-space-8);
  border-radius: var(--ds-radius-lg);
}

@media (max-width: 575px) {
  .ds-demo-stage {
    padding: var(--ds-space-5);
  }
}

/* The fact strip without the hero overlap. */
.ds-facts.ds-demo-flat {
  margin-top: 0;
}

.ds-demo-card-title--on-dark {
  color: var(--ds-ink-on-dark-strong);
}
.ds-demo-card-text--on-dark {
  color: var(--ds-ink-on-dark-body);
}

/* A demo label that opens a new block further down a section. */
.ds-demo-label.ds-demo-label--spaced {
  margin-top: var(--ds-space-10);
}

/* Typography specimens. */
.ds-demo-type {
  font-family: var(--ds-font-display);
  font-weight: var(--ds-fw-bold);
  line-height: var(--ds-lh-tight);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--ds-ink);
}
.ds-demo-type--hero-dramatic {
  font-size: var(--ds-text-hero-dramatic);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-display);
  letter-spacing: var(--ds-tracking-display);
}
.ds-demo-type--hero-standard {
  font-size: var(--ds-text-hero-standard);
  font-weight: var(--ds-fw-black);
  letter-spacing: var(--ds-tracking-display);
}
.ds-demo-type--section {
  font-size: var(--ds-text-section);
  font-weight: var(--ds-fw-black);
}
.ds-demo-type--title-3 {
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-black);
}
.ds-demo-type--subsection {
  font-size: var(--ds-text-subsection);
  font-weight: var(--ds-fw-semi);
}
.ds-demo-type--page-title {
  font-size: var(--ds-text-page-title);
  letter-spacing: -0.01em;
}
.ds-demo-type--lead {
  margin: 0;
  font-family: var(--ds-font-body);
  font-size: var(--ds-text-lead);
  font-weight: var(--ds-fw-regular);
  line-height: var(--ds-lh-base);
  letter-spacing: normal;
  color: var(--ds-ink-soft);
}
.ds-demo-type--numbers {
  margin: 0;
  font-family: var(--ds-font-body);
  font-size: var(--ds-text-title-3);
  font-weight: var(--ds-fw-semi);
  letter-spacing: normal;
  font-variant-numeric: tabular-nums;
}
.ds-demo-type--body {
  font-family: var(--ds-font-body);
  font-size: var(--ds-text-body);
  font-weight: var(--ds-fw-regular);
  line-height: var(--ds-lh-prose);
  letter-spacing: normal;
  color: var(--ds-ink-soft);
  max-width: 640px;
  margin: 0;
}
.ds-demo-type--caption {
  margin-bottom: var(--ds-space-3);
  font-family: var(--ds-font-body);
  font-size: var(--ds-text-caption);
  font-weight: var(--ds-fw-regular);
  letter-spacing: normal;
  line-height: var(--ds-lh-base);
  color: var(--ds-ink-mute);
}

/* Dashed info callout (scroll probe, replay playground). */
.ds-demo-callout {
  margin-top: var(--ds-space-8);
  padding: var(--ds-space-5);
  background: var(--ds-surface-tint);
  border-radius: var(--ds-radius-md);
  border: 1px dashed var(--ds-border);
}

/* Radius / shadow token tiles. */
.ds-demo-radius--xs {
  border-radius: var(--ds-radius-xs);
}
.ds-demo-radius--sm {
  border-radius: var(--ds-radius-sm);
}
.ds-demo-radius--md {
  border-radius: var(--ds-radius-md);
}
.ds-demo-radius--lg {
  border-radius: var(--ds-radius-lg);
}
.ds-demo-radius--xl {
  border-radius: var(--ds-radius-xl);
}
.ds-demo-radius--full {
  border-radius: var(--ds-radius-full);
}
.ds-demo-shadow--sm {
  box-shadow: var(--ds-shadow-sm);
  border: 1px solid var(--ds-border-subtle);
}
.ds-demo-shadow--md {
  box-shadow: var(--ds-shadow-md);
}
.ds-demo-shadow--lg {
  box-shadow: var(--ds-shadow-lg);
}
.ds-demo-shadow--focus {
  box-shadow: var(--ds-shadow-focus);
}

/* Spacing token bars. */
.ds-demo-space-row {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  font-size: var(--ds-text-caption);
}
.ds-demo-space-row code {
  min-width: 120px;
}
.ds-demo-space-bar {
  background: var(--ds-blue);
  height: 12px;
}
.ds-demo-space-bar--1 {
  width: var(--ds-space-1);
}
.ds-demo-space-bar--2 {
  width: var(--ds-space-2);
}
.ds-demo-space-bar--3 {
  width: var(--ds-space-3);
}
.ds-demo-space-bar--4 {
  width: var(--ds-space-4);
}
.ds-demo-space-bar--6 {
  width: var(--ds-space-6);
}
.ds-demo-space-bar--8 {
  width: var(--ds-space-8);
}
.ds-demo-space-bar--12 {
  width: var(--ds-space-12);
}
.ds-demo-space-bar--16 {
  width: var(--ds-space-16);
}
.ds-demo-space-bar--24 {
  width: var(--ds-space-24);
}

/* Static modal showcase: the fixed-position overlay component renders inside
   a relative frame instead of covering the viewport. */
.ds-demo-modal {
  position: relative;
  min-height: 380px;
  border-radius: var(--ds-radius-md);
  overflow: hidden;
  border: 1px solid var(--ds-border-subtle);
}
.ds-demo-modal .ds-modal {
  position: absolute;
}

/* Static consent-banner showcase: neutralize the fixed positioning. */
.ds-demo-consent .ds-consent {
  position: static;
  margin: 0;
  max-width: 520px;
}

/* Static filter-bar showcase: the open popover panel renders in-flow under
   its toggle instead of absolutely overlapping the sections below. */
.ds-demo-filter .ds-filter-group__panel {
  position: static;
  margin-top: var(--ds-space-2);
  box-shadow: var(--ds-shadow-md);
}

/* Neutralizes sticky/viewport-bound positioning on embedded demos (e.g. the
   sticky ds-panel--sidebar). Same specificity as the component rules but
   defined later in the file, so the cascade resolves without !important. */
.ds-demo-static {
  position: static;
  max-height: none;
}

/* Static toast showcase: suppress the animation entry state. */
.ds-demo-toast-stack {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  max-width: 380px;
}
.ds-demo-toast-stack .ls-toast {
  opacity: 1;
  transform: none;
  position: static;
}

/* Skeleton-loader showcase lines (the product sizes them contextually). */
.ds-demo-skel-line {
  max-width: 320px;
  margin-bottom: var(--ds-space-2);
}
.ds-demo-skel-line--short {
  max-width: 200px;
}

/* Cross-reference links inside demo lists join the 44px touch floor; the
   negative margin keeps the list rhythm while the hit box grows. */
@media (max-width: 767px) {
  .ds-demo-list a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-block: -12px;
  }
}

/* ============================================================================
 * Reduced-Motion (accessibility — honors the OS "reduce motion" preference)
 * Neutralizes the decorative keyframe loops (orbs, blobs, particles, beams)
 * and collapses transitions so users who request reduced motion get a still
 * experience. The JS motion layers already self-disable; this covers the
 * CSS-only decorative animations. The !important resets are exempt from the
 * design gates' budget (see tests/test_design_gates.py).
 * ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================================
 * Print-Styles (primarily for /app/billing/invoices/ rendering)
 * Hide decorative chrome, force high-contrast ink on white, full-width tables.
 * ========================================================================== */

@media print {
  /* Force reveal/stagger elements visible (IO/GSAP don't fire in print mode) */
  .ds-reveal,
  .ds-stagger-parent > *,
  .ds-stagger-child-gsap {
    opacity: 1 !important;
    transform: none !important;
    visibility: visible !important;
  }

  /* Kill all decorative chrome + motion */
  .ds-site-header,
  .ds-site-footer,
  .ds-site-header__progress,
  .ds-cta-band,
  .ds-hero__bg,
  .ds-hero-mockup,
  .ds-trust-strip,
  .ds-consent,
  .ds-sticky-cta,
  [data-motion],
  .ds-op-orb,
  .ds-op-pattern-dots,
  .ds-op-pattern-hexa,
  .ds-op-pattern-sparkle,
  .ds-hero-beam,
  .ds-hero-spot,
  .ds-hero-particles,
  .ds-hero-conic,
  .ds-hero-grid-pattern,
  .ds-orb {
    display: none !important;
  }

  /* Reset layout shell for print */
  body.ds-layout-shell,
  html {
    background: var(--ds-white) !important;
    color: var(--ds-black) !important;
    font-family: var(--ds-font-body);
  }

  main.ds-layout-shell__body {
    padding: 0 !important;
  }

  /* Full-width sections, no background */
  .ds-section,
  .ds-hero,
  .ds-hero--standard,
  .ds-hero--dramatic {
    background: none !important;
    padding: 0 !important;
    min-height: 0 !important;
    margin-top: 0 !important;
    display: block !important;
  }

  .ds-section__inner {
    max-width: 100% !important;
    padding: 0 !important;
    margin: 0 !important;
  }

  /* Force dark text on any hero/dark-section */
  .ds-hero__title,
  .ds-hero__lead,
  .ds-section__title,
  .ds-section__lead,
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  li,
  td,
  th {
    color: var(--ds-black) !important;
    background: transparent !important;
  }

  /* Kill shadows, hover-transforms */
  .ds-card,
  .ds-panel,
  .ds-button {
    box-shadow: none !important;
    transform: none !important;
  }

  /* Tables (especially invoices): full-width, bordered, no background stripes */
  .ds-compare-table,
  table,
  .ds-card--content table {
    width: 100% !important;
    border-collapse: collapse !important;
    box-shadow: none !important;
  }
  .ds-compare-table th,
  .ds-compare-table td,
  table th,
  table td {
    border: 1px solid var(--ds-ink) !important;
    padding: 6px 10px !important;
    background: none !important;
  }
  .ds-compare-table th,
  table th {
    background: var(--ds-surface-muted) !important;
    font-weight: var(--ds-fw-bold);
  }

  /* Buttons: hide action-buttons in print (download PDF, pay, etc.) */
  .ds-button--primary,
  .ds-button--danger,
  button[type="submit"] {
    display: none !important;
  }
  .ds-button--secondary,
  .ds-button--ghost {
    border: 1px solid var(--ds-ink) !important;
    color: var(--ds-black) !important;
    background: transparent !important;
  }

  /* Gradient text becomes solid ink in print */
  .ds-gradient-text,
  .ds-gradient-text--on-dark {
    background: none !important;
    -webkit-text-fill-color: var(--ds-black) !important;
    color: var(--ds-black) !important;
  }

  /* Badges: solid black-and-white for print */
  .ds-badge {
    border: 1px solid var(--ds-black) !important;
    background: var(--ds-white) !important;
    color: var(--ds-black) !important;
  }

  /* Avoid page-break inside cards and table rows */
  .ds-card,
  tr,
  .ds-empty-state {
    page-break-inside: avoid;
  }

  /* Section-breaks: allow natural flow */
  .ds-section {
    page-break-before: auto;
    page-break-after: auto;
  }

  /* Print-only: show URLs after links for reference */
  a[href]:not([href^="#"])::after {
    content: " (" attr(href) ")";
    font-size: 0.8em;
    color: var(--ds-ink-soft);
  }

  /* Disable all animations and transitions */
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}
